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.

Files changed (135) 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/roadmapit_command.py +10 -0
  11. doit_cli/cli/status_command.py +117 -0
  12. doit_cli/cli/sync_prompts_command.py +248 -0
  13. doit_cli/cli/validate_command.py +196 -0
  14. doit_cli/cli/verify_command.py +204 -0
  15. doit_cli/cli/workflow_mixin.py +224 -0
  16. doit_cli/cli/xref_command.py +555 -0
  17. doit_cli/formatters/__init__.py +8 -0
  18. doit_cli/formatters/base.py +38 -0
  19. doit_cli/formatters/json_formatter.py +126 -0
  20. doit_cli/formatters/markdown_formatter.py +97 -0
  21. doit_cli/formatters/rich_formatter.py +257 -0
  22. doit_cli/main.py +51 -0
  23. doit_cli/models/__init__.py +139 -0
  24. doit_cli/models/agent.py +74 -0
  25. doit_cli/models/analytics_models.py +384 -0
  26. doit_cli/models/context_config.py +464 -0
  27. doit_cli/models/crossref_models.py +182 -0
  28. doit_cli/models/diagram_models.py +363 -0
  29. doit_cli/models/fixit_models.py +355 -0
  30. doit_cli/models/hook_config.py +125 -0
  31. doit_cli/models/project.py +91 -0
  32. doit_cli/models/results.py +121 -0
  33. doit_cli/models/search_models.py +228 -0
  34. doit_cli/models/status_models.py +195 -0
  35. doit_cli/models/sync_models.py +146 -0
  36. doit_cli/models/template.py +77 -0
  37. doit_cli/models/validation_models.py +175 -0
  38. doit_cli/models/workflow_models.py +319 -0
  39. doit_cli/prompts/__init__.py +5 -0
  40. doit_cli/prompts/fixit_prompts.py +344 -0
  41. doit_cli/prompts/interactive.py +390 -0
  42. doit_cli/rules/__init__.py +5 -0
  43. doit_cli/rules/builtin_rules.py +160 -0
  44. doit_cli/services/__init__.py +79 -0
  45. doit_cli/services/agent_detector.py +168 -0
  46. doit_cli/services/analytics_service.py +218 -0
  47. doit_cli/services/architecture_generator.py +290 -0
  48. doit_cli/services/backup_service.py +204 -0
  49. doit_cli/services/config_loader.py +113 -0
  50. doit_cli/services/context_loader.py +1123 -0
  51. doit_cli/services/coverage_calculator.py +142 -0
  52. doit_cli/services/crossref_service.py +237 -0
  53. doit_cli/services/cycle_time_calculator.py +134 -0
  54. doit_cli/services/date_inferrer.py +349 -0
  55. doit_cli/services/diagram_service.py +337 -0
  56. doit_cli/services/drift_detector.py +109 -0
  57. doit_cli/services/entity_parser.py +301 -0
  58. doit_cli/services/er_diagram_generator.py +197 -0
  59. doit_cli/services/fixit_service.py +699 -0
  60. doit_cli/services/github_service.py +192 -0
  61. doit_cli/services/hook_manager.py +258 -0
  62. doit_cli/services/hook_validator.py +528 -0
  63. doit_cli/services/input_validator.py +322 -0
  64. doit_cli/services/memory_search.py +527 -0
  65. doit_cli/services/mermaid_validator.py +334 -0
  66. doit_cli/services/prompt_transformer.py +91 -0
  67. doit_cli/services/prompt_writer.py +133 -0
  68. doit_cli/services/query_interpreter.py +428 -0
  69. doit_cli/services/report_exporter.py +219 -0
  70. doit_cli/services/report_generator.py +256 -0
  71. doit_cli/services/requirement_parser.py +112 -0
  72. doit_cli/services/roadmap_summarizer.py +209 -0
  73. doit_cli/services/rule_engine.py +443 -0
  74. doit_cli/services/scaffolder.py +215 -0
  75. doit_cli/services/score_calculator.py +172 -0
  76. doit_cli/services/section_parser.py +204 -0
  77. doit_cli/services/spec_scanner.py +327 -0
  78. doit_cli/services/state_manager.py +355 -0
  79. doit_cli/services/status_reporter.py +143 -0
  80. doit_cli/services/task_parser.py +347 -0
  81. doit_cli/services/template_manager.py +710 -0
  82. doit_cli/services/template_reader.py +158 -0
  83. doit_cli/services/user_journey_generator.py +214 -0
  84. doit_cli/services/user_story_parser.py +232 -0
  85. doit_cli/services/validation_service.py +188 -0
  86. doit_cli/services/validator.py +232 -0
  87. doit_cli/services/velocity_tracker.py +173 -0
  88. doit_cli/services/workflow_engine.py +405 -0
  89. doit_cli/templates/agent-file-template.md +28 -0
  90. doit_cli/templates/checklist-template.md +39 -0
  91. doit_cli/templates/commands/doit.checkin.md +363 -0
  92. doit_cli/templates/commands/doit.constitution.md +187 -0
  93. doit_cli/templates/commands/doit.documentit.md +485 -0
  94. doit_cli/templates/commands/doit.fixit.md +181 -0
  95. doit_cli/templates/commands/doit.implementit.md +265 -0
  96. doit_cli/templates/commands/doit.planit.md +262 -0
  97. doit_cli/templates/commands/doit.reviewit.md +355 -0
  98. doit_cli/templates/commands/doit.roadmapit.md +389 -0
  99. doit_cli/templates/commands/doit.scaffoldit.md +458 -0
  100. doit_cli/templates/commands/doit.specit.md +521 -0
  101. doit_cli/templates/commands/doit.taskit.md +304 -0
  102. doit_cli/templates/commands/doit.testit.md +277 -0
  103. doit_cli/templates/config/context.yaml +134 -0
  104. doit_cli/templates/config/hooks.yaml +93 -0
  105. doit_cli/templates/config/validation-rules.yaml +64 -0
  106. doit_cli/templates/github-issue-templates/epic.yml +78 -0
  107. doit_cli/templates/github-issue-templates/feature.yml +116 -0
  108. doit_cli/templates/github-issue-templates/task.yml +129 -0
  109. doit_cli/templates/hooks/.gitkeep +0 -0
  110. doit_cli/templates/hooks/post-commit.sh +25 -0
  111. doit_cli/templates/hooks/post-merge.sh +75 -0
  112. doit_cli/templates/hooks/pre-commit.sh +17 -0
  113. doit_cli/templates/hooks/pre-push.sh +18 -0
  114. doit_cli/templates/memory/completed_roadmap.md +50 -0
  115. doit_cli/templates/memory/constitution.md +125 -0
  116. doit_cli/templates/memory/roadmap.md +61 -0
  117. doit_cli/templates/plan-template.md +146 -0
  118. doit_cli/templates/scripts/bash/check-prerequisites.sh +166 -0
  119. doit_cli/templates/scripts/bash/common.sh +156 -0
  120. doit_cli/templates/scripts/bash/create-new-feature.sh +297 -0
  121. doit_cli/templates/scripts/bash/setup-plan.sh +61 -0
  122. doit_cli/templates/scripts/bash/update-agent-context.sh +675 -0
  123. doit_cli/templates/scripts/powershell/check-prerequisites.ps1 +148 -0
  124. doit_cli/templates/scripts/powershell/common.ps1 +137 -0
  125. doit_cli/templates/scripts/powershell/create-new-feature.ps1 +283 -0
  126. doit_cli/templates/scripts/powershell/setup-plan.ps1 +61 -0
  127. doit_cli/templates/scripts/powershell/update-agent-context.ps1 +406 -0
  128. doit_cli/templates/spec-template.md +159 -0
  129. doit_cli/templates/tasks-template.md +313 -0
  130. doit_cli/templates/vscode-settings.json +14 -0
  131. doit_toolkit_cli-0.1.10.dist-info/METADATA +324 -0
  132. doit_toolkit_cli-0.1.10.dist-info/RECORD +135 -0
  133. doit_toolkit_cli-0.1.10.dist-info/WHEEL +4 -0
  134. doit_toolkit_cli-0.1.10.dist-info/entry_points.txt +2 -0
  135. doit_toolkit_cli-0.1.10.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,675 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Update agent context files with information from plan.md
4
+ #
5
+ # This script maintains AI agent context files by parsing feature specifications
6
+ # and updating agent-specific configuration files with project information.
7
+ #
8
+ # MAIN FUNCTIONS:
9
+ # 1. Environment Validation
10
+ # - Verifies git repository structure and branch information
11
+ # - Checks for required plan.md files and templates
12
+ # - Validates file permissions and accessibility
13
+ #
14
+ # 2. Plan Data Extraction
15
+ # - Parses plan.md files to extract project metadata
16
+ # - Identifies language/version, frameworks, databases, and project types
17
+ # - Handles missing or incomplete specification data gracefully
18
+ #
19
+ # 3. Agent File Management
20
+ # - Creates new agent context files from templates when needed
21
+ # - Updates existing agent files with new project information
22
+ # - Preserves manual additions and custom configurations
23
+ # - Supports multiple AI agent formats and directory structures
24
+ #
25
+ # 4. Content Generation
26
+ # - Generates language-specific build/test commands
27
+ # - Creates appropriate project directory structures
28
+ # - Updates technology stacks and recent changes sections
29
+ # - Maintains consistent formatting and timestamps
30
+ #
31
+ # 5. Multi-Agent Support
32
+ # - Handles agent-specific file paths and naming conventions
33
+ # - Supports: Claude Code, GitHub Copilot
34
+ # - Can update single agents or all existing agent files
35
+ # - Creates default Claude file if no agent files exist
36
+ #
37
+ # Usage: ./update-agent-context.sh [agent_type]
38
+ # Agent types: claude|copilot
39
+ # Leave empty to update all existing agent files
40
+
41
+ set -e
42
+
43
+ # Enable strict error handling
44
+ set -u
45
+ set -o pipefail
46
+
47
+ #==============================================================================
48
+ # Configuration and Global Variables
49
+ #==============================================================================
50
+
51
+ # Get script directory and load common functions
52
+ SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
53
+ source "$SCRIPT_DIR/common.sh"
54
+
55
+ # Get all paths and variables from common functions
56
+ eval $(get_feature_paths)
57
+
58
+ NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code
59
+ AGENT_TYPE="${1:-}"
60
+
61
+ # Agent-specific file paths
62
+ CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
63
+ COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md"
64
+
65
+ # Template file
66
+ TEMPLATE_FILE="$REPO_ROOT/.doit/templates/agent-file-template.md"
67
+
68
+ # Global variables for parsed plan data
69
+ NEW_LANG=""
70
+ NEW_FRAMEWORK=""
71
+ NEW_DB=""
72
+ NEW_PROJECT_TYPE=""
73
+
74
+ #==============================================================================
75
+ # Utility Functions
76
+ #==============================================================================
77
+
78
+ log_info() {
79
+ echo "INFO: $1"
80
+ }
81
+
82
+ log_success() {
83
+ echo "✓ $1"
84
+ }
85
+
86
+ log_error() {
87
+ echo "ERROR: $1" >&2
88
+ }
89
+
90
+ log_warning() {
91
+ echo "WARNING: $1" >&2
92
+ }
93
+
94
+ # Cleanup function for temporary files
95
+ cleanup() {
96
+ local exit_code=$?
97
+ rm -f /tmp/agent_update_*_$$
98
+ rm -f /tmp/manual_additions_$$
99
+ exit $exit_code
100
+ }
101
+
102
+ # Set up cleanup trap
103
+ trap cleanup EXIT INT TERM
104
+
105
+ #==============================================================================
106
+ # Validation Functions
107
+ #==============================================================================
108
+
109
+ validate_environment() {
110
+ # Check if we have a current branch/feature (git or non-git)
111
+ if [[ -z "$CURRENT_BRANCH" ]]; then
112
+ log_error "Unable to determine current feature"
113
+ if [[ "$HAS_GIT" == "true" ]]; then
114
+ log_info "Make sure you're on a feature branch"
115
+ else
116
+ log_info "Set SPECIFY_FEATURE environment variable or create a feature first"
117
+ fi
118
+ exit 1
119
+ fi
120
+
121
+ # Check if plan.md exists
122
+ if [[ ! -f "$NEW_PLAN" ]]; then
123
+ log_error "No plan.md found at $NEW_PLAN"
124
+ log_info "Make sure you're working on a feature with a corresponding spec directory"
125
+ if [[ "$HAS_GIT" != "true" ]]; then
126
+ log_info "Use: export SPECIFY_FEATURE=your-feature-name or create a new feature first"
127
+ fi
128
+ exit 1
129
+ fi
130
+
131
+ # Check if template exists (needed for new files)
132
+ if [[ ! -f "$TEMPLATE_FILE" ]]; then
133
+ log_warning "Template file not found at $TEMPLATE_FILE"
134
+ log_warning "Creating new agent files will fail"
135
+ fi
136
+ }
137
+
138
+ #==============================================================================
139
+ # Plan Parsing Functions
140
+ #==============================================================================
141
+
142
+ extract_plan_field() {
143
+ local field_pattern="$1"
144
+ local plan_file="$2"
145
+
146
+ grep "^\*\*${field_pattern}\*\*: " "$plan_file" 2>/dev/null | \
147
+ head -1 | \
148
+ sed "s|^\*\*${field_pattern}\*\*: ||" | \
149
+ sed 's/^[ \t]*//;s/[ \t]*$//' | \
150
+ grep -v "NEEDS CLARIFICATION" | \
151
+ grep -v "^N/A$" || echo ""
152
+ }
153
+
154
+ parse_plan_data() {
155
+ local plan_file="$1"
156
+
157
+ if [[ ! -f "$plan_file" ]]; then
158
+ log_error "Plan file not found: $plan_file"
159
+ return 1
160
+ fi
161
+
162
+ if [[ ! -r "$plan_file" ]]; then
163
+ log_error "Plan file is not readable: $plan_file"
164
+ return 1
165
+ fi
166
+
167
+ log_info "Parsing plan data from $plan_file"
168
+
169
+ NEW_LANG=$(extract_plan_field "Language/Version" "$plan_file")
170
+ NEW_FRAMEWORK=$(extract_plan_field "Primary Dependencies" "$plan_file")
171
+ NEW_DB=$(extract_plan_field "Storage" "$plan_file")
172
+ NEW_PROJECT_TYPE=$(extract_plan_field "Project Type" "$plan_file")
173
+
174
+ # Log what we found
175
+ if [[ -n "$NEW_LANG" ]]; then
176
+ log_info "Found language: $NEW_LANG"
177
+ else
178
+ log_warning "No language information found in plan"
179
+ fi
180
+
181
+ if [[ -n "$NEW_FRAMEWORK" ]]; then
182
+ log_info "Found framework: $NEW_FRAMEWORK"
183
+ fi
184
+
185
+ if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then
186
+ log_info "Found database: $NEW_DB"
187
+ fi
188
+
189
+ if [[ -n "$NEW_PROJECT_TYPE" ]]; then
190
+ log_info "Found project type: $NEW_PROJECT_TYPE"
191
+ fi
192
+ }
193
+
194
+ format_technology_stack() {
195
+ local lang="$1"
196
+ local framework="$2"
197
+ local parts=()
198
+
199
+ # Add non-empty parts
200
+ [[ -n "$lang" && "$lang" != "NEEDS CLARIFICATION" ]] && parts+=("$lang")
201
+ [[ -n "$framework" && "$framework" != "NEEDS CLARIFICATION" && "$framework" != "N/A" ]] && parts+=("$framework")
202
+
203
+ # Join with proper formatting
204
+ if [[ ${#parts[@]} -eq 0 ]]; then
205
+ echo ""
206
+ elif [[ ${#parts[@]} -eq 1 ]]; then
207
+ echo "${parts[0]}"
208
+ else
209
+ # Join multiple parts with " + "
210
+ local result="${parts[0]}"
211
+ for ((i=1; i<${#parts[@]}; i++)); do
212
+ result="$result + ${parts[i]}"
213
+ done
214
+ echo "$result"
215
+ fi
216
+ }
217
+
218
+ #==============================================================================
219
+ # Template and Content Generation Functions
220
+ #==============================================================================
221
+
222
+ get_project_structure() {
223
+ local project_type="$1"
224
+
225
+ if [[ "$project_type" == *"web"* ]]; then
226
+ echo "backend/\\nfrontend/\\ntests/"
227
+ else
228
+ echo "src/\\ntests/"
229
+ fi
230
+ }
231
+
232
+ get_commands_for_language() {
233
+ local lang="$1"
234
+
235
+ case "$lang" in
236
+ *"Python"*)
237
+ echo "cd src && pytest && ruff check ."
238
+ ;;
239
+ *"Rust"*)
240
+ echo "cargo test && cargo clippy"
241
+ ;;
242
+ *"JavaScript"*|*"TypeScript"*)
243
+ echo "npm test \\&\\& npm run lint"
244
+ ;;
245
+ *)
246
+ echo "# Add commands for $lang"
247
+ ;;
248
+ esac
249
+ }
250
+
251
+ get_language_conventions() {
252
+ local lang="$1"
253
+ echo "$lang: Follow standard conventions"
254
+ }
255
+
256
+ create_new_agent_file() {
257
+ local target_file="$1"
258
+ local temp_file="$2"
259
+ local project_name="$3"
260
+ local current_date="$4"
261
+
262
+ if [[ ! -f "$TEMPLATE_FILE" ]]; then
263
+ log_error "Template not found at $TEMPLATE_FILE"
264
+ return 1
265
+ fi
266
+
267
+ if [[ ! -r "$TEMPLATE_FILE" ]]; then
268
+ log_error "Template file is not readable: $TEMPLATE_FILE"
269
+ return 1
270
+ fi
271
+
272
+ log_info "Creating new agent context file from template..."
273
+
274
+ if ! cp "$TEMPLATE_FILE" "$temp_file"; then
275
+ log_error "Failed to copy template file"
276
+ return 1
277
+ fi
278
+
279
+ # Replace template placeholders
280
+ local project_structure
281
+ project_structure=$(get_project_structure "$NEW_PROJECT_TYPE")
282
+
283
+ local commands
284
+ commands=$(get_commands_for_language "$NEW_LANG")
285
+
286
+ local language_conventions
287
+ language_conventions=$(get_language_conventions "$NEW_LANG")
288
+
289
+ # Perform substitutions with error checking using safer approach
290
+ # Escape special characters for sed by using a different delimiter or escaping
291
+ local escaped_lang=$(printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|]/\\&/g')
292
+ local escaped_framework=$(printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|]/\\&/g')
293
+ local escaped_branch=$(printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|]/\\&/g')
294
+
295
+ # Build technology stack and recent change strings conditionally
296
+ local tech_stack
297
+ if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then
298
+ tech_stack="- $escaped_lang + $escaped_framework ($escaped_branch)"
299
+ elif [[ -n "$escaped_lang" ]]; then
300
+ tech_stack="- $escaped_lang ($escaped_branch)"
301
+ elif [[ -n "$escaped_framework" ]]; then
302
+ tech_stack="- $escaped_framework ($escaped_branch)"
303
+ else
304
+ tech_stack="- ($escaped_branch)"
305
+ fi
306
+
307
+ local recent_change
308
+ if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then
309
+ recent_change="- $escaped_branch: Added $escaped_lang + $escaped_framework"
310
+ elif [[ -n "$escaped_lang" ]]; then
311
+ recent_change="- $escaped_branch: Added $escaped_lang"
312
+ elif [[ -n "$escaped_framework" ]]; then
313
+ recent_change="- $escaped_branch: Added $escaped_framework"
314
+ else
315
+ recent_change="- $escaped_branch: Added"
316
+ fi
317
+
318
+ local substitutions=(
319
+ "s|\[PROJECT NAME\]|$project_name|"
320
+ "s|\[DATE\]|$current_date|"
321
+ "s|\[EXTRACTED FROM ALL PLAN.MD FILES\]|$tech_stack|"
322
+ "s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g"
323
+ "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|"
324
+ "s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|"
325
+ "s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|$recent_change|"
326
+ )
327
+
328
+ for substitution in "${substitutions[@]}"; do
329
+ if ! sed -i.bak -e "$substitution" "$temp_file"; then
330
+ log_error "Failed to perform substitution: $substitution"
331
+ rm -f "$temp_file" "$temp_file.bak"
332
+ return 1
333
+ fi
334
+ done
335
+
336
+ # Convert \n sequences to actual newlines
337
+ newline=$(printf '\n')
338
+ sed -i.bak2 "s/\\\\n/${newline}/g" "$temp_file"
339
+
340
+ # Clean up backup files
341
+ rm -f "$temp_file.bak" "$temp_file.bak2"
342
+
343
+ return 0
344
+ }
345
+
346
+
347
+
348
+
349
+ update_existing_agent_file() {
350
+ local target_file="$1"
351
+ local current_date="$2"
352
+
353
+ log_info "Updating existing agent context file..."
354
+
355
+ # Use a single temporary file for atomic update
356
+ local temp_file
357
+ temp_file=$(mktemp) || {
358
+ log_error "Failed to create temporary file"
359
+ return 1
360
+ }
361
+
362
+ # Process the file in one pass
363
+ local tech_stack=$(format_technology_stack "$NEW_LANG" "$NEW_FRAMEWORK")
364
+ local new_tech_entries=()
365
+ local new_change_entry=""
366
+
367
+ # Prepare new technology entries
368
+ if [[ -n "$tech_stack" ]] && ! grep -q "$tech_stack" "$target_file"; then
369
+ new_tech_entries+=("- $tech_stack ($CURRENT_BRANCH)")
370
+ fi
371
+
372
+ if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]] && ! grep -q "$NEW_DB" "$target_file"; then
373
+ new_tech_entries+=("- $NEW_DB ($CURRENT_BRANCH)")
374
+ fi
375
+
376
+ # Prepare new change entry
377
+ if [[ -n "$tech_stack" ]]; then
378
+ new_change_entry="- $CURRENT_BRANCH: Added $tech_stack"
379
+ elif [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]]; then
380
+ new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB"
381
+ fi
382
+
383
+ # Check if sections exist in the file
384
+ local has_active_technologies=0
385
+ local has_recent_changes=0
386
+
387
+ if grep -q "^## Active Technologies" "$target_file" 2>/dev/null; then
388
+ has_active_technologies=1
389
+ fi
390
+
391
+ if grep -q "^## Recent Changes" "$target_file" 2>/dev/null; then
392
+ has_recent_changes=1
393
+ fi
394
+
395
+ # Process file line by line
396
+ local in_tech_section=false
397
+ local in_changes_section=false
398
+ local tech_entries_added=false
399
+ local changes_entries_added=false
400
+ local existing_changes_count=0
401
+ local file_ended=false
402
+
403
+ while IFS= read -r line || [[ -n "$line" ]]; do
404
+ # Handle Active Technologies section
405
+ if [[ "$line" == "## Active Technologies" ]]; then
406
+ echo "$line" >> "$temp_file"
407
+ in_tech_section=true
408
+ continue
409
+ elif [[ $in_tech_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then
410
+ # Add new tech entries before closing the section
411
+ if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
412
+ printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
413
+ tech_entries_added=true
414
+ fi
415
+ echo "$line" >> "$temp_file"
416
+ in_tech_section=false
417
+ continue
418
+ elif [[ $in_tech_section == true ]] && [[ -z "$line" ]]; then
419
+ # Add new tech entries before empty line in tech section
420
+ if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
421
+ printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
422
+ tech_entries_added=true
423
+ fi
424
+ echo "$line" >> "$temp_file"
425
+ continue
426
+ fi
427
+
428
+ # Handle Recent Changes section
429
+ if [[ "$line" == "## Recent Changes" ]]; then
430
+ echo "$line" >> "$temp_file"
431
+ # Add new change entry right after the heading
432
+ if [[ -n "$new_change_entry" ]]; then
433
+ echo "$new_change_entry" >> "$temp_file"
434
+ fi
435
+ in_changes_section=true
436
+ changes_entries_added=true
437
+ continue
438
+ elif [[ $in_changes_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then
439
+ echo "$line" >> "$temp_file"
440
+ in_changes_section=false
441
+ continue
442
+ elif [[ $in_changes_section == true ]] && [[ "$line" == "- "* ]]; then
443
+ # Keep only first 2 existing changes
444
+ if [[ $existing_changes_count -lt 2 ]]; then
445
+ echo "$line" >> "$temp_file"
446
+ ((existing_changes_count++))
447
+ fi
448
+ continue
449
+ fi
450
+
451
+ # Update timestamp
452
+ if [[ "$line" =~ \*\*Last\ updated\*\*:.*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then
453
+ echo "$line" | sed "s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/$current_date/" >> "$temp_file"
454
+ else
455
+ echo "$line" >> "$temp_file"
456
+ fi
457
+ done < "$target_file"
458
+
459
+ # Post-loop check: if we're still in the Active Technologies section and haven't added new entries
460
+ if [[ $in_tech_section == true ]] && [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
461
+ printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
462
+ tech_entries_added=true
463
+ fi
464
+
465
+ # If sections don't exist, add them at the end of the file
466
+ if [[ $has_active_technologies -eq 0 ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
467
+ echo "" >> "$temp_file"
468
+ echo "## Active Technologies" >> "$temp_file"
469
+ printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
470
+ tech_entries_added=true
471
+ fi
472
+
473
+ if [[ $has_recent_changes -eq 0 ]] && [[ -n "$new_change_entry" ]]; then
474
+ echo "" >> "$temp_file"
475
+ echo "## Recent Changes" >> "$temp_file"
476
+ echo "$new_change_entry" >> "$temp_file"
477
+ changes_entries_added=true
478
+ fi
479
+
480
+ # Move temp file to target atomically
481
+ if ! mv "$temp_file" "$target_file"; then
482
+ log_error "Failed to update target file"
483
+ rm -f "$temp_file"
484
+ return 1
485
+ fi
486
+
487
+ return 0
488
+ }
489
+ #==============================================================================
490
+ # Main Agent File Update Function
491
+ #==============================================================================
492
+
493
+ update_agent_file() {
494
+ local target_file="$1"
495
+ local agent_name="$2"
496
+
497
+ if [[ -z "$target_file" ]] || [[ -z "$agent_name" ]]; then
498
+ log_error "update_agent_file requires target_file and agent_name parameters"
499
+ return 1
500
+ fi
501
+
502
+ log_info "Updating $agent_name context file: $target_file"
503
+
504
+ local project_name
505
+ project_name=$(basename "$REPO_ROOT")
506
+ local current_date
507
+ current_date=$(date +%Y-%m-%d)
508
+
509
+ # Create directory if it doesn't exist
510
+ local target_dir
511
+ target_dir=$(dirname "$target_file")
512
+ if [[ ! -d "$target_dir" ]]; then
513
+ if ! mkdir -p "$target_dir"; then
514
+ log_error "Failed to create directory: $target_dir"
515
+ return 1
516
+ fi
517
+ fi
518
+
519
+ if [[ ! -f "$target_file" ]]; then
520
+ # Create new file from template
521
+ local temp_file
522
+ temp_file=$(mktemp) || {
523
+ log_error "Failed to create temporary file"
524
+ return 1
525
+ }
526
+
527
+ if create_new_agent_file "$target_file" "$temp_file" "$project_name" "$current_date"; then
528
+ if mv "$temp_file" "$target_file"; then
529
+ log_success "Created new $agent_name context file"
530
+ else
531
+ log_error "Failed to move temporary file to $target_file"
532
+ rm -f "$temp_file"
533
+ return 1
534
+ fi
535
+ else
536
+ log_error "Failed to create new agent file"
537
+ rm -f "$temp_file"
538
+ return 1
539
+ fi
540
+ else
541
+ # Update existing file
542
+ if [[ ! -r "$target_file" ]]; then
543
+ log_error "Cannot read existing file: $target_file"
544
+ return 1
545
+ fi
546
+
547
+ if [[ ! -w "$target_file" ]]; then
548
+ log_error "Cannot write to existing file: $target_file"
549
+ return 1
550
+ fi
551
+
552
+ if update_existing_agent_file "$target_file" "$current_date"; then
553
+ log_success "Updated existing $agent_name context file"
554
+ else
555
+ log_error "Failed to update existing agent file"
556
+ return 1
557
+ fi
558
+ fi
559
+
560
+ return 0
561
+ }
562
+
563
+ #==============================================================================
564
+ # Agent Selection and Processing
565
+ #==============================================================================
566
+
567
+ update_specific_agent() {
568
+ local agent_type="$1"
569
+
570
+ case "$agent_type" in
571
+ claude)
572
+ update_agent_file "$CLAUDE_FILE" "Claude Code"
573
+ ;;
574
+ copilot)
575
+ update_agent_file "$COPILOT_FILE" "GitHub Copilot"
576
+ ;;
577
+ *)
578
+ log_error "Unknown agent type '$agent_type'"
579
+ log_error "Expected: claude|copilot"
580
+ exit 1
581
+ ;;
582
+ esac
583
+ }
584
+
585
+ update_all_existing_agents() {
586
+ local found_agent=false
587
+
588
+ # Check each possible agent file and update if it exists
589
+ if [[ -f "$CLAUDE_FILE" ]]; then
590
+ update_agent_file "$CLAUDE_FILE" "Claude Code"
591
+ found_agent=true
592
+ fi
593
+
594
+ if [[ -f "$COPILOT_FILE" ]]; then
595
+ update_agent_file "$COPILOT_FILE" "GitHub Copilot"
596
+ found_agent=true
597
+ fi
598
+
599
+ # If no agent files exist, create a default Claude file
600
+ if [[ "$found_agent" == false ]]; then
601
+ log_info "No existing agent files found, creating default Claude file..."
602
+ update_agent_file "$CLAUDE_FILE" "Claude Code"
603
+ fi
604
+ }
605
+ print_summary() {
606
+ echo
607
+ log_info "Summary of changes:"
608
+
609
+ if [[ -n "$NEW_LANG" ]]; then
610
+ echo " - Added language: $NEW_LANG"
611
+ fi
612
+
613
+ if [[ -n "$NEW_FRAMEWORK" ]]; then
614
+ echo " - Added framework: $NEW_FRAMEWORK"
615
+ fi
616
+
617
+ if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then
618
+ echo " - Added database: $NEW_DB"
619
+ fi
620
+
621
+ echo
622
+
623
+ log_info "Usage: $0 [claude|copilot]"
624
+ }
625
+
626
+ #==============================================================================
627
+ # Main Execution
628
+ #==============================================================================
629
+
630
+ main() {
631
+ # Validate environment before proceeding
632
+ validate_environment
633
+
634
+ log_info "=== Updating agent context files for feature $CURRENT_BRANCH ==="
635
+
636
+ # Parse the plan file to extract project information
637
+ if ! parse_plan_data "$NEW_PLAN"; then
638
+ log_error "Failed to parse plan data"
639
+ exit 1
640
+ fi
641
+
642
+ # Process based on agent type argument
643
+ local success=true
644
+
645
+ if [[ -z "$AGENT_TYPE" ]]; then
646
+ # No specific agent provided - update all existing agent files
647
+ log_info "No agent specified, updating all existing agent files..."
648
+ if ! update_all_existing_agents; then
649
+ success=false
650
+ fi
651
+ else
652
+ # Specific agent provided - update only that agent
653
+ log_info "Updating specific agent: $AGENT_TYPE"
654
+ if ! update_specific_agent "$AGENT_TYPE"; then
655
+ success=false
656
+ fi
657
+ fi
658
+
659
+ # Print summary
660
+ print_summary
661
+
662
+ if [[ "$success" == true ]]; then
663
+ log_success "Agent context update completed successfully"
664
+ exit 0
665
+ else
666
+ log_error "Agent context update completed with errors"
667
+ exit 1
668
+ fi
669
+ }
670
+
671
+ # Execute main function if script is run directly
672
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
673
+ main "$@"
674
+ fi
675
+