doit-toolkit-cli 0.1.10__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.
Potentially problematic release.
This version of doit-toolkit-cli might be problematic. Click here for more details.
- doit_cli/__init__.py +1356 -0
- doit_cli/cli/__init__.py +26 -0
- doit_cli/cli/analytics_command.py +616 -0
- doit_cli/cli/context_command.py +213 -0
- doit_cli/cli/diagram_command.py +304 -0
- doit_cli/cli/fixit_command.py +641 -0
- doit_cli/cli/hooks_command.py +211 -0
- doit_cli/cli/init_command.py +613 -0
- doit_cli/cli/memory_command.py +293 -0
- doit_cli/cli/roadmapit_command.py +10 -0
- doit_cli/cli/status_command.py +117 -0
- doit_cli/cli/sync_prompts_command.py +248 -0
- doit_cli/cli/validate_command.py +196 -0
- doit_cli/cli/verify_command.py +204 -0
- doit_cli/cli/workflow_mixin.py +224 -0
- doit_cli/cli/xref_command.py +555 -0
- doit_cli/formatters/__init__.py +8 -0
- doit_cli/formatters/base.py +38 -0
- doit_cli/formatters/json_formatter.py +126 -0
- doit_cli/formatters/markdown_formatter.py +97 -0
- doit_cli/formatters/rich_formatter.py +257 -0
- doit_cli/main.py +51 -0
- doit_cli/models/__init__.py +139 -0
- doit_cli/models/agent.py +74 -0
- doit_cli/models/analytics_models.py +384 -0
- doit_cli/models/context_config.py +464 -0
- doit_cli/models/crossref_models.py +182 -0
- doit_cli/models/diagram_models.py +363 -0
- doit_cli/models/fixit_models.py +355 -0
- doit_cli/models/hook_config.py +125 -0
- doit_cli/models/project.py +91 -0
- doit_cli/models/results.py +121 -0
- doit_cli/models/search_models.py +228 -0
- doit_cli/models/status_models.py +195 -0
- doit_cli/models/sync_models.py +146 -0
- doit_cli/models/template.py +77 -0
- doit_cli/models/validation_models.py +175 -0
- doit_cli/models/workflow_models.py +319 -0
- doit_cli/prompts/__init__.py +5 -0
- doit_cli/prompts/fixit_prompts.py +344 -0
- doit_cli/prompts/interactive.py +390 -0
- doit_cli/rules/__init__.py +5 -0
- doit_cli/rules/builtin_rules.py +160 -0
- doit_cli/services/__init__.py +79 -0
- doit_cli/services/agent_detector.py +168 -0
- doit_cli/services/analytics_service.py +218 -0
- doit_cli/services/architecture_generator.py +290 -0
- doit_cli/services/backup_service.py +204 -0
- doit_cli/services/config_loader.py +113 -0
- doit_cli/services/context_loader.py +1123 -0
- doit_cli/services/coverage_calculator.py +142 -0
- doit_cli/services/crossref_service.py +237 -0
- doit_cli/services/cycle_time_calculator.py +134 -0
- doit_cli/services/date_inferrer.py +349 -0
- doit_cli/services/diagram_service.py +337 -0
- doit_cli/services/drift_detector.py +109 -0
- doit_cli/services/entity_parser.py +301 -0
- doit_cli/services/er_diagram_generator.py +197 -0
- doit_cli/services/fixit_service.py +699 -0
- doit_cli/services/github_service.py +192 -0
- doit_cli/services/hook_manager.py +258 -0
- doit_cli/services/hook_validator.py +528 -0
- doit_cli/services/input_validator.py +322 -0
- doit_cli/services/memory_search.py +527 -0
- doit_cli/services/mermaid_validator.py +334 -0
- doit_cli/services/prompt_transformer.py +91 -0
- doit_cli/services/prompt_writer.py +133 -0
- doit_cli/services/query_interpreter.py +428 -0
- doit_cli/services/report_exporter.py +219 -0
- doit_cli/services/report_generator.py +256 -0
- doit_cli/services/requirement_parser.py +112 -0
- doit_cli/services/roadmap_summarizer.py +209 -0
- doit_cli/services/rule_engine.py +443 -0
- doit_cli/services/scaffolder.py +215 -0
- doit_cli/services/score_calculator.py +172 -0
- doit_cli/services/section_parser.py +204 -0
- doit_cli/services/spec_scanner.py +327 -0
- doit_cli/services/state_manager.py +355 -0
- doit_cli/services/status_reporter.py +143 -0
- doit_cli/services/task_parser.py +347 -0
- doit_cli/services/template_manager.py +710 -0
- doit_cli/services/template_reader.py +158 -0
- doit_cli/services/user_journey_generator.py +214 -0
- doit_cli/services/user_story_parser.py +232 -0
- doit_cli/services/validation_service.py +188 -0
- doit_cli/services/validator.py +232 -0
- doit_cli/services/velocity_tracker.py +173 -0
- doit_cli/services/workflow_engine.py +405 -0
- doit_cli/templates/agent-file-template.md +28 -0
- doit_cli/templates/checklist-template.md +39 -0
- doit_cli/templates/commands/doit.checkin.md +363 -0
- doit_cli/templates/commands/doit.constitution.md +187 -0
- doit_cli/templates/commands/doit.documentit.md +485 -0
- doit_cli/templates/commands/doit.fixit.md +181 -0
- doit_cli/templates/commands/doit.implementit.md +265 -0
- doit_cli/templates/commands/doit.planit.md +262 -0
- doit_cli/templates/commands/doit.reviewit.md +355 -0
- doit_cli/templates/commands/doit.roadmapit.md +389 -0
- doit_cli/templates/commands/doit.scaffoldit.md +458 -0
- doit_cli/templates/commands/doit.specit.md +521 -0
- doit_cli/templates/commands/doit.taskit.md +304 -0
- doit_cli/templates/commands/doit.testit.md +277 -0
- doit_cli/templates/config/context.yaml +134 -0
- doit_cli/templates/config/hooks.yaml +93 -0
- doit_cli/templates/config/validation-rules.yaml +64 -0
- doit_cli/templates/github-issue-templates/epic.yml +78 -0
- doit_cli/templates/github-issue-templates/feature.yml +116 -0
- doit_cli/templates/github-issue-templates/task.yml +129 -0
- doit_cli/templates/hooks/.gitkeep +0 -0
- doit_cli/templates/hooks/post-commit.sh +25 -0
- doit_cli/templates/hooks/post-merge.sh +75 -0
- doit_cli/templates/hooks/pre-commit.sh +17 -0
- doit_cli/templates/hooks/pre-push.sh +18 -0
- doit_cli/templates/memory/completed_roadmap.md +50 -0
- doit_cli/templates/memory/constitution.md +125 -0
- doit_cli/templates/memory/roadmap.md +61 -0
- doit_cli/templates/plan-template.md +146 -0
- doit_cli/templates/scripts/bash/check-prerequisites.sh +166 -0
- doit_cli/templates/scripts/bash/common.sh +156 -0
- doit_cli/templates/scripts/bash/create-new-feature.sh +297 -0
- doit_cli/templates/scripts/bash/setup-plan.sh +61 -0
- doit_cli/templates/scripts/bash/update-agent-context.sh +675 -0
- doit_cli/templates/scripts/powershell/check-prerequisites.ps1 +148 -0
- doit_cli/templates/scripts/powershell/common.ps1 +137 -0
- doit_cli/templates/scripts/powershell/create-new-feature.ps1 +283 -0
- doit_cli/templates/scripts/powershell/setup-plan.ps1 +61 -0
- doit_cli/templates/scripts/powershell/update-agent-context.ps1 +406 -0
- doit_cli/templates/spec-template.md +159 -0
- doit_cli/templates/tasks-template.md +313 -0
- doit_cli/templates/vscode-settings.json +14 -0
- doit_toolkit_cli-0.1.10.dist-info/METADATA +324 -0
- doit_toolkit_cli-0.1.10.dist-info/RECORD +135 -0
- doit_toolkit_cli-0.1.10.dist-info/WHEEL +4 -0
- doit_toolkit_cli-0.1.10.dist-info/entry_points.txt +2 -0
- doit_toolkit_cli-0.1.10.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"""Workflow engine for orchestrating guided workflows.
|
|
2
|
+
|
|
3
|
+
This module provides the WorkflowEngine class that manages workflow execution,
|
|
4
|
+
step navigation, state persistence, and user input collection.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import signal
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from ..models.workflow_models import (
|
|
14
|
+
Workflow,
|
|
15
|
+
WorkflowStep,
|
|
16
|
+
WorkflowState,
|
|
17
|
+
WorkflowStatus,
|
|
18
|
+
StepResponse,
|
|
19
|
+
ValidationResult,
|
|
20
|
+
WorkflowError,
|
|
21
|
+
NavigationCommand,
|
|
22
|
+
)
|
|
23
|
+
from ..prompts.interactive import InteractivePrompt, ProgressDisplay
|
|
24
|
+
from .input_validator import validate_step, get_validator
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# =============================================================================
|
|
28
|
+
# WorkflowEngine Protocol
|
|
29
|
+
# =============================================================================
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@runtime_checkable
|
|
33
|
+
class WorkflowEngineProtocol(Protocol):
|
|
34
|
+
"""Protocol defining the WorkflowEngine interface."""
|
|
35
|
+
|
|
36
|
+
def start(self, workflow: Workflow) -> WorkflowState:
|
|
37
|
+
"""Start a new workflow or resume interrupted one."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def execute_step(
|
|
41
|
+
self,
|
|
42
|
+
state: WorkflowState,
|
|
43
|
+
step: WorkflowStep,
|
|
44
|
+
) -> tuple[WorkflowState, StepResponse]:
|
|
45
|
+
"""Execute a single workflow step."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def complete(self, state: WorkflowState) -> dict:
|
|
49
|
+
"""Complete workflow and return collected responses."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def cancel(self, state: WorkflowState) -> None:
|
|
53
|
+
"""Cancel workflow and save state for resume."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# =============================================================================
|
|
58
|
+
# WorkflowEngine Implementation
|
|
59
|
+
# =============================================================================
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class WorkflowEngine:
|
|
63
|
+
"""Orchestrates guided workflow execution.
|
|
64
|
+
|
|
65
|
+
Manages the full lifecycle of a workflow including:
|
|
66
|
+
- Starting new workflows or resuming interrupted ones
|
|
67
|
+
- Executing individual steps with validation
|
|
68
|
+
- Handling navigation (back/skip)
|
|
69
|
+
- State persistence for recovery
|
|
70
|
+
- Progress display
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
console: Console | None = None,
|
|
76
|
+
state_manager: "StateManager | None" = None,
|
|
77
|
+
):
|
|
78
|
+
"""Initialize the workflow engine.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
console: Rich console for output
|
|
82
|
+
state_manager: Optional state manager for persistence
|
|
83
|
+
"""
|
|
84
|
+
self.console = console or Console()
|
|
85
|
+
self.state_manager = state_manager
|
|
86
|
+
self.prompt = InteractivePrompt(console)
|
|
87
|
+
self.progress = ProgressDisplay(console)
|
|
88
|
+
self._interrupted = False
|
|
89
|
+
|
|
90
|
+
def start(self, workflow: Workflow) -> WorkflowState:
|
|
91
|
+
"""Start a new workflow or resume interrupted one.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
workflow: Workflow definition to execute
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Initial or resumed WorkflowState
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
WorkflowError: If workflow cannot be started
|
|
101
|
+
"""
|
|
102
|
+
# Check for existing interrupted state
|
|
103
|
+
if self.state_manager:
|
|
104
|
+
existing_state = self.state_manager.load(workflow.command_name)
|
|
105
|
+
if existing_state and existing_state.status == WorkflowStatus.INTERRUPTED:
|
|
106
|
+
if self._prompt_resume(existing_state):
|
|
107
|
+
existing_state.status = WorkflowStatus.RUNNING
|
|
108
|
+
return existing_state
|
|
109
|
+
else:
|
|
110
|
+
# User chose to start fresh
|
|
111
|
+
self.state_manager.delete(existing_state)
|
|
112
|
+
|
|
113
|
+
# Create new state
|
|
114
|
+
state = WorkflowState(
|
|
115
|
+
id=self._generate_state_id(workflow.command_name),
|
|
116
|
+
workflow_id=workflow.id,
|
|
117
|
+
command_name=workflow.command_name,
|
|
118
|
+
current_step=0,
|
|
119
|
+
total_steps=len(workflow.steps),
|
|
120
|
+
status=WorkflowStatus.PENDING,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return state
|
|
124
|
+
|
|
125
|
+
def execute_step(
|
|
126
|
+
self,
|
|
127
|
+
state: WorkflowState,
|
|
128
|
+
step: WorkflowStep,
|
|
129
|
+
) -> tuple[WorkflowState, StepResponse]:
|
|
130
|
+
"""Execute a single workflow step.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
state: Current workflow state
|
|
134
|
+
step: Step to execute
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Updated state and step response
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
NavigationCommand: If user navigates back/skip
|
|
141
|
+
KeyboardInterrupt: If user cancels
|
|
142
|
+
"""
|
|
143
|
+
# Update status to running
|
|
144
|
+
if state.status == WorkflowStatus.PENDING:
|
|
145
|
+
state.status = WorkflowStatus.RUNNING
|
|
146
|
+
|
|
147
|
+
# Get validator if specified
|
|
148
|
+
validator = None
|
|
149
|
+
if step.validation_type:
|
|
150
|
+
validator = get_validator(step.validation_type)
|
|
151
|
+
|
|
152
|
+
# Show progress
|
|
153
|
+
total_steps = self._count_steps(state)
|
|
154
|
+
self.progress.show_step(step, state.current_step + 1, total_steps)
|
|
155
|
+
|
|
156
|
+
# Get input based on step type
|
|
157
|
+
if step.options:
|
|
158
|
+
value = self.prompt.prompt_choice(step, step.options)
|
|
159
|
+
else:
|
|
160
|
+
value = self.prompt.prompt(step, validator)
|
|
161
|
+
|
|
162
|
+
# Create response
|
|
163
|
+
response = StepResponse(
|
|
164
|
+
step_id=step.id,
|
|
165
|
+
value=value,
|
|
166
|
+
skipped=False,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Update state
|
|
170
|
+
state.set_response(response)
|
|
171
|
+
state.advance_step()
|
|
172
|
+
|
|
173
|
+
# Show completion
|
|
174
|
+
self.progress.mark_complete(step)
|
|
175
|
+
|
|
176
|
+
return state, response
|
|
177
|
+
|
|
178
|
+
def run(
|
|
179
|
+
self,
|
|
180
|
+
workflow: Workflow,
|
|
181
|
+
initial_responses: dict[str, str] | None = None,
|
|
182
|
+
) -> dict:
|
|
183
|
+
"""Run a complete workflow from start to finish.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
workflow: Workflow to execute
|
|
187
|
+
initial_responses: Optional pre-populated responses to skip steps.
|
|
188
|
+
Maps step_id -> value. Steps with initial responses will be
|
|
189
|
+
skipped and use the provided value.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Dictionary of step_id -> response value
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
WorkflowError: If workflow fails
|
|
196
|
+
KeyboardInterrupt: If user cancels
|
|
197
|
+
"""
|
|
198
|
+
# Setup interrupt handler
|
|
199
|
+
self._setup_interrupt_handler()
|
|
200
|
+
|
|
201
|
+
state = self.start(workflow)
|
|
202
|
+
|
|
203
|
+
# Sort steps by order
|
|
204
|
+
steps = sorted(workflow.steps, key=lambda s: s.order)
|
|
205
|
+
|
|
206
|
+
# Pre-populate state with initial responses (Fix MT-007)
|
|
207
|
+
if initial_responses:
|
|
208
|
+
for step_id, value in initial_responses.items():
|
|
209
|
+
state.responses[step_id] = StepResponse(
|
|
210
|
+
step_id=step_id,
|
|
211
|
+
value=value,
|
|
212
|
+
skipped=False,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
while state.current_step < len(steps):
|
|
217
|
+
if self._interrupted:
|
|
218
|
+
self._handle_interrupt(state)
|
|
219
|
+
raise KeyboardInterrupt
|
|
220
|
+
|
|
221
|
+
step = steps[state.current_step]
|
|
222
|
+
|
|
223
|
+
# Skip steps that already have responses (Fix MT-007)
|
|
224
|
+
if step.id in state.responses:
|
|
225
|
+
self.console.print(
|
|
226
|
+
f"[dim]Skipping {step.name} (provided via CLI)[/dim]"
|
|
227
|
+
)
|
|
228
|
+
state.current_step += 1
|
|
229
|
+
continue
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
state, response = self.execute_step(state, step)
|
|
233
|
+
except NavigationCommand as nav:
|
|
234
|
+
state = self._handle_navigation(state, nav, steps)
|
|
235
|
+
|
|
236
|
+
# Save state after each step (if state manager available)
|
|
237
|
+
if self.state_manager:
|
|
238
|
+
self.state_manager.save(state)
|
|
239
|
+
|
|
240
|
+
# Complete workflow
|
|
241
|
+
return self.complete(state)
|
|
242
|
+
|
|
243
|
+
except KeyboardInterrupt:
|
|
244
|
+
self.cancel(state)
|
|
245
|
+
raise
|
|
246
|
+
|
|
247
|
+
def complete(self, state: WorkflowState) -> dict:
|
|
248
|
+
"""Complete workflow and return collected responses.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
state: Final workflow state
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Dictionary of step_id -> response value
|
|
255
|
+
"""
|
|
256
|
+
state.status = WorkflowStatus.COMPLETED
|
|
257
|
+
|
|
258
|
+
# Delete state file on success
|
|
259
|
+
if self.state_manager:
|
|
260
|
+
self.state_manager.delete(state)
|
|
261
|
+
|
|
262
|
+
# Show summary
|
|
263
|
+
self.progress.show_summary(len(state.responses))
|
|
264
|
+
|
|
265
|
+
# Return responses as simple dict
|
|
266
|
+
return {
|
|
267
|
+
step_id: resp.value
|
|
268
|
+
for step_id, resp in state.responses.items()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
def cancel(self, state: WorkflowState) -> None:
|
|
272
|
+
"""Cancel workflow and save state for resume.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
state: Current workflow state
|
|
276
|
+
"""
|
|
277
|
+
state.status = WorkflowStatus.INTERRUPTED
|
|
278
|
+
|
|
279
|
+
# Save state for resume
|
|
280
|
+
if self.state_manager:
|
|
281
|
+
self.state_manager.save(state)
|
|
282
|
+
|
|
283
|
+
# Show interrupted message
|
|
284
|
+
total_steps = self._count_steps(state)
|
|
285
|
+
self.progress.show_interrupted(state.current_step + 1, total_steps)
|
|
286
|
+
|
|
287
|
+
# =========================================================================
|
|
288
|
+
# Navigation Handling (T011)
|
|
289
|
+
# =========================================================================
|
|
290
|
+
|
|
291
|
+
def _handle_navigation(
|
|
292
|
+
self,
|
|
293
|
+
state: WorkflowState,
|
|
294
|
+
nav: NavigationCommand,
|
|
295
|
+
steps: list[WorkflowStep],
|
|
296
|
+
) -> WorkflowState:
|
|
297
|
+
"""Handle navigation commands (back/skip).
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
state: Current workflow state
|
|
301
|
+
nav: Navigation command
|
|
302
|
+
steps: List of workflow steps
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Updated workflow state
|
|
306
|
+
"""
|
|
307
|
+
if nav.command == "back":
|
|
308
|
+
return self._go_back(state, steps)
|
|
309
|
+
elif nav.command == "skip":
|
|
310
|
+
return self._skip_step(state, steps)
|
|
311
|
+
return state
|
|
312
|
+
|
|
313
|
+
def _go_back(
|
|
314
|
+
self,
|
|
315
|
+
state: WorkflowState,
|
|
316
|
+
steps: list[WorkflowStep],
|
|
317
|
+
) -> WorkflowState:
|
|
318
|
+
"""Go back to the previous step.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
state: Current workflow state
|
|
322
|
+
steps: List of workflow steps
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Updated state with decreased step index
|
|
326
|
+
"""
|
|
327
|
+
if state.current_step > 0:
|
|
328
|
+
state.go_back()
|
|
329
|
+
self.console.print("[info]Going back to previous step...[/info]")
|
|
330
|
+
else:
|
|
331
|
+
self.console.print("[warning]Already at first step[/warning]")
|
|
332
|
+
return state
|
|
333
|
+
|
|
334
|
+
def _skip_step(
|
|
335
|
+
self,
|
|
336
|
+
state: WorkflowState,
|
|
337
|
+
steps: list[WorkflowStep],
|
|
338
|
+
) -> WorkflowState:
|
|
339
|
+
"""Skip the current optional step.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
state: Current workflow state
|
|
343
|
+
steps: List of workflow steps
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Updated state with step skipped
|
|
347
|
+
"""
|
|
348
|
+
step = steps[state.current_step]
|
|
349
|
+
|
|
350
|
+
if step.required:
|
|
351
|
+
self.console.print("[error]Cannot skip required step[/error]")
|
|
352
|
+
return state
|
|
353
|
+
|
|
354
|
+
# Create skip response with default value
|
|
355
|
+
response = StepResponse(
|
|
356
|
+
step_id=step.id,
|
|
357
|
+
value=step.default_value or "",
|
|
358
|
+
skipped=True,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
state.set_response(response)
|
|
362
|
+
state.advance_step()
|
|
363
|
+
self.progress.mark_skipped(step)
|
|
364
|
+
|
|
365
|
+
return state
|
|
366
|
+
|
|
367
|
+
# =========================================================================
|
|
368
|
+
# Internal Methods
|
|
369
|
+
# =========================================================================
|
|
370
|
+
|
|
371
|
+
def _generate_state_id(self, command_name: str) -> str:
|
|
372
|
+
"""Generate a unique state ID."""
|
|
373
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
374
|
+
return f"{command_name}_{timestamp}"
|
|
375
|
+
|
|
376
|
+
def _count_steps(self, state: WorkflowState) -> int:
|
|
377
|
+
"""Get total step count from state."""
|
|
378
|
+
if state.total_steps > 0:
|
|
379
|
+
return state.total_steps
|
|
380
|
+
# Fallback for backwards compatibility
|
|
381
|
+
return max(len(state.responses) + 1, state.current_step + 1)
|
|
382
|
+
|
|
383
|
+
def _prompt_resume(self, state: WorkflowState) -> bool:
|
|
384
|
+
"""Prompt user to resume interrupted workflow."""
|
|
385
|
+
self.console.print()
|
|
386
|
+
self.console.print(
|
|
387
|
+
f"[warning]Found interrupted workflow from "
|
|
388
|
+
f"{state.updated_at.strftime('%Y-%m-%d %H:%M')}[/warning]"
|
|
389
|
+
)
|
|
390
|
+
self.console.print(
|
|
391
|
+
f"[info]Progress: {len(state.responses)} steps completed[/info]"
|
|
392
|
+
)
|
|
393
|
+
return self.prompt.prompt_confirm("Resume where you left off?", default=True)
|
|
394
|
+
|
|
395
|
+
def _setup_interrupt_handler(self) -> None:
|
|
396
|
+
"""Setup signal handler for Ctrl+C."""
|
|
397
|
+
def handler(signum, frame):
|
|
398
|
+
self._interrupted = True
|
|
399
|
+
|
|
400
|
+
signal.signal(signal.SIGINT, handler)
|
|
401
|
+
|
|
402
|
+
def _handle_interrupt(self, state: WorkflowState) -> None:
|
|
403
|
+
"""Handle interrupt signal."""
|
|
404
|
+
self._interrupted = False
|
|
405
|
+
self.cancel(state)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# [PROJECT NAME] Development Guidelines
|
|
2
|
+
|
|
3
|
+
Auto-generated from all feature plans. Last updated: [DATE]
|
|
4
|
+
|
|
5
|
+
## Active Technologies
|
|
6
|
+
|
|
7
|
+
[EXTRACTED FROM ALL PLAN.MD FILES]
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
[ACTUAL STRUCTURE FROM PLANS]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]
|
|
18
|
+
|
|
19
|
+
## Code Style
|
|
20
|
+
|
|
21
|
+
[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]
|
|
22
|
+
|
|
23
|
+
## Recent Changes
|
|
24
|
+
|
|
25
|
+
[LAST 3 FEATURES AND WHAT THEY ADDED]
|
|
26
|
+
|
|
27
|
+
<!-- MANUAL ADDITIONS START -->
|
|
28
|
+
<!-- MANUAL ADDITIONS END -->
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# [CHECKLIST TYPE] Checklist: [FEATURE NAME]
|
|
2
|
+
|
|
3
|
+
**Purpose**: [Brief description of what this checklist covers]
|
|
4
|
+
**Created**: [DATE]
|
|
5
|
+
**Feature**: [Link to spec.md or relevant documentation]
|
|
6
|
+
|
|
7
|
+
**Note**: This checklist is generated by `/doit.specit` as part of spec validation.
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
============================================================================
|
|
11
|
+
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
|
|
12
|
+
|
|
13
|
+
The checklist generation process MUST replace these with actual items based on:
|
|
14
|
+
- Feature requirements from spec.md
|
|
15
|
+
- Technical context from plan.md
|
|
16
|
+
- Implementation details from tasks.md
|
|
17
|
+
|
|
18
|
+
DO NOT keep these sample items in the generated checklist file.
|
|
19
|
+
============================================================================
|
|
20
|
+
-->
|
|
21
|
+
|
|
22
|
+
## [Category 1]
|
|
23
|
+
|
|
24
|
+
- [ ] CHK001 First checklist item with clear action
|
|
25
|
+
- [ ] CHK002 Second checklist item
|
|
26
|
+
- [ ] CHK003 Third checklist item
|
|
27
|
+
|
|
28
|
+
## [Category 2]
|
|
29
|
+
|
|
30
|
+
- [ ] CHK004 Another category item
|
|
31
|
+
- [ ] CHK005 Item with specific criteria
|
|
32
|
+
- [ ] CHK006 Final item in this category
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
- Check items off as completed: `[x]`
|
|
37
|
+
- Add comments or findings inline
|
|
38
|
+
- Link to relevant resources or documentation
|
|
39
|
+
- Items are numbered sequentially for easy reference
|