claude-autopm 1.18.0 → 1.20.0

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 (75) hide show
  1. package/README.md +159 -0
  2. package/autopm/.claude/agents/core/mcp-manager.md +1 -1
  3. package/autopm/.claude/commands/pm/context.md +11 -0
  4. package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
  5. package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
  6. package/autopm/.claude/commands/pm/epic-start.md +19 -0
  7. package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
  8. package/autopm/.claude/commands/pm/epic-sync.md +14 -14
  9. package/autopm/.claude/commands/pm/issue-start.md +50 -5
  10. package/autopm/.claude/commands/pm/issue-sync.md +15 -15
  11. package/autopm/.claude/commands/pm/what-next.md +11 -0
  12. package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
  13. package/autopm/.claude/scripts/azure/active-work.js +2 -2
  14. package/autopm/.claude/scripts/azure/blocked.js +13 -13
  15. package/autopm/.claude/scripts/azure/daily.js +1 -1
  16. package/autopm/.claude/scripts/azure/dashboard.js +1 -1
  17. package/autopm/.claude/scripts/azure/feature-list.js +2 -2
  18. package/autopm/.claude/scripts/azure/feature-status.js +1 -1
  19. package/autopm/.claude/scripts/azure/next-task.js +1 -1
  20. package/autopm/.claude/scripts/azure/search.js +1 -1
  21. package/autopm/.claude/scripts/azure/setup.js +15 -15
  22. package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
  23. package/autopm/.claude/scripts/azure/sync.js +1 -1
  24. package/autopm/.claude/scripts/azure/us-list.js +1 -1
  25. package/autopm/.claude/scripts/azure/us-status.js +1 -1
  26. package/autopm/.claude/scripts/azure/validate.js +13 -13
  27. package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
  28. package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
  29. package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
  30. package/autopm/.claude/scripts/pm/context.js +338 -0
  31. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
  32. package/autopm/.claude/scripts/pm/lib/README.md +85 -0
  33. package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
  34. package/autopm/.claude/scripts/pm/next.js +25 -1
  35. package/autopm/.claude/scripts/pm/what-next.js +660 -0
  36. package/bin/autopm.js +25 -0
  37. package/package.json +1 -1
  38. package/lib/agentExecutor.js.deprecated +0 -101
  39. package/lib/azure/cache.js +0 -80
  40. package/lib/azure/client.js +0 -77
  41. package/lib/azure/formatter.js +0 -177
  42. package/lib/commandHelpers.js +0 -177
  43. package/lib/context/manager.js +0 -290
  44. package/lib/documentation/manager.js +0 -528
  45. package/lib/github/workflow-manager.js +0 -546
  46. package/lib/helpers/azure-batch-api.js +0 -133
  47. package/lib/helpers/azure-cache-manager.js +0 -287
  48. package/lib/helpers/azure-parallel-processor.js +0 -158
  49. package/lib/helpers/azure-work-item-create.js +0 -278
  50. package/lib/helpers/gh-issue-create.js +0 -250
  51. package/lib/helpers/interactive-prompt.js +0 -336
  52. package/lib/helpers/output-manager.js +0 -335
  53. package/lib/helpers/progress-indicator.js +0 -258
  54. package/lib/performance/benchmarker.js +0 -429
  55. package/lib/pm/epic-decomposer.js +0 -273
  56. package/lib/pm/epic-syncer.js +0 -221
  57. package/lib/prdMetadata.js +0 -270
  58. package/lib/providers/azure/index.js +0 -234
  59. package/lib/providers/factory.js +0 -87
  60. package/lib/providers/github/index.js +0 -204
  61. package/lib/providers/interface.js +0 -73
  62. package/lib/python/scaffold-manager.js +0 -576
  63. package/lib/react/scaffold-manager.js +0 -745
  64. package/lib/regression/analyzer.js +0 -578
  65. package/lib/release/manager.js +0 -324
  66. package/lib/tailwind/manager.js +0 -486
  67. package/lib/traefik/manager.js +0 -484
  68. package/lib/utils/colors.js +0 -126
  69. package/lib/utils/config.js +0 -317
  70. package/lib/utils/filesystem.js +0 -316
  71. package/lib/utils/logger.js +0 -135
  72. package/lib/utils/prompts.js +0 -294
  73. package/lib/utils/shell.js +0 -237
  74. package/lib/validators/email-validator.js +0 -337
  75. package/lib/workflow/manager.js +0 -449
@@ -33,7 +33,7 @@ Run comprehensive validation checks before syncing:
33
33
 
34
34
  ```bash
35
35
  # Run preflight validation
36
- bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS"
36
+ bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS"
37
37
 
38
38
  if [[ $? -ne 0 ]]; then
39
39
  echo "❌ Preflight validation failed"
@@ -41,9 +41,9 @@ if [[ $? -ne 0 ]]; then
41
41
  fi
42
42
 
43
43
  # Extract validated paths from preflight output
44
- epic_name=$(bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Epic:" | cut -d: -f2- | xargs)
45
- updates_dir=$(bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Updates Directory:" | cut -d: -f2- | xargs)
46
- progress_file=$(bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Progress File:" | cut -d: -f2- | xargs)
44
+ epic_name=$(bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Epic:" | cut -d: -f2- | xargs)
45
+ updates_dir=$(bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Updates Directory:" | cut -d: -f2- | xargs)
46
+ progress_file=$(bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ARGUMENTS" | grep "Progress File:" | cut -d: -f2- | xargs)
47
47
 
48
48
  echo "✅ Preflight checks passed"
49
49
  ```
@@ -65,7 +65,7 @@ Collect all local development updates:
65
65
  last_sync=$(grep '^last_sync:' "$progress_file" | sed 's/^last_sync: *//')
66
66
 
67
67
  # Gather all updates since last sync
68
- consolidated_updates=$(bash autopm/.claude/scripts/pm/issue-sync/gather-updates.sh \
68
+ consolidated_updates=$(bash .claude/scripts/pm/issue-sync/gather-updates.sh \
69
69
  "$ARGUMENTS" \
70
70
  "$updates_dir" \
71
71
  "$last_sync")
@@ -95,7 +95,7 @@ if [[ "$completion" == "100" ]]; then
95
95
  fi
96
96
 
97
97
  # Format the comment
98
- formatted_comment=$(bash autopm/.claude/scripts/pm/issue-sync/format-comment.sh \
98
+ formatted_comment=$(bash .claude/scripts/pm/issue-sync/format-comment.sh \
99
99
  "$ARGUMENTS" \
100
100
  "$consolidated_updates" \
101
101
  "$progress_file" \
@@ -119,7 +119,7 @@ Post the formatted comment to GitHub:
119
119
 
120
120
  ```bash
121
121
  # Post comment to GitHub issue
122
- comment_url=$(bash autopm/.claude/scripts/pm/issue-sync/post-comment.sh \
122
+ comment_url=$(bash .claude/scripts/pm/issue-sync/post-comment.sh \
123
123
  "$ARGUMENTS" \
124
124
  "$formatted_comment" \
125
125
  "$is_completion")
@@ -146,7 +146,7 @@ Update local metadata after successful sync:
146
146
 
147
147
  ```bash
148
148
  # Update progress.md frontmatter with sync information
149
- bash autopm/.claude/scripts/pm/issue-sync/update-frontmatter.sh \
149
+ bash .claude/scripts/pm/issue-sync/update-frontmatter.sh \
150
150
  "$ARGUMENTS" \
151
151
  "$progress_file" \
152
152
  "$comment_url" \
@@ -178,13 +178,13 @@ echo "🚀 Starting modular issue sync for: #$ISSUE_NUMBER"
178
178
 
179
179
  # Step 1: Preflight validation
180
180
  echo "🔍 Running preflight validation..."
181
- if ! bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER"; then
181
+ if ! bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER"; then
182
182
  echo "❌ Preflight validation failed"
183
183
  exit 1
184
184
  fi
185
185
 
186
186
  # Extract paths from a single preflight run
187
- preflight_output=$(bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER")
187
+ preflight_output=$(bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER")
188
188
  epic_name=$(echo "$preflight_output" | grep "Epic:" | cut -d: -f2- | xargs)
189
189
  updates_dir=$(echo "$preflight_output" | grep "Updates Directory:" | cut -d: -f2- | xargs)
190
190
  progress_file=$(echo "$preflight_output" | grep "Progress File:" | cut -d: -f2- | xargs)
@@ -196,7 +196,7 @@ echo " Updates: $updates_dir"
196
196
  # Step 2: Gather updates
197
197
  echo "📝 Gathering local updates..."
198
198
  last_sync=$(grep '^last_sync:' "$progress_file" 2>/dev/null | sed 's/^last_sync: *//' || echo "")
199
- consolidated_updates=$(bash autopm/.claude/scripts/pm/issue-sync/gather-updates.sh \
199
+ consolidated_updates=$(bash .claude/scripts/pm/issue-sync/gather-updates.sh \
200
200
  "$ISSUE_NUMBER" \
201
201
  "$updates_dir" \
202
202
  "$last_sync")
@@ -217,7 +217,7 @@ if [[ "$completion" == "100" ]]; then
217
217
  echo " Task is complete - formatting completion comment"
218
218
  fi
219
219
 
220
- formatted_comment=$(bash autopm/.claude/scripts/pm/issue-sync/format-comment.sh \
220
+ formatted_comment=$(bash .claude/scripts/pm/issue-sync/format-comment.sh \
221
221
  "$ISSUE_NUMBER" \
222
222
  "$consolidated_updates" \
223
223
  "$progress_file" \
@@ -232,7 +232,7 @@ echo "✅ Comment formatted"
232
232
 
233
233
  # Step 4: Post to GitHub
234
234
  echo "☁️ Posting to GitHub..."
235
- comment_url=$(bash autopm/.claude/scripts/pm/issue-sync/post-comment.sh \
235
+ comment_url=$(bash .claude/scripts/pm/issue-sync/post-comment.sh \
236
236
  "$ISSUE_NUMBER" \
237
237
  "$formatted_comment" \
238
238
  "$is_completion")
@@ -247,7 +247,7 @@ echo "✅ Comment posted successfully"
247
247
 
248
248
  # Step 5: Update frontmatter
249
249
  echo "📝 Updating local metadata..."
250
- bash autopm/.claude/scripts/pm/issue-sync/update-frontmatter.sh \
250
+ bash .claude/scripts/pm/issue-sync/update-frontmatter.sh \
251
251
  "$ISSUE_NUMBER" \
252
252
  "$progress_file" \
253
253
  "$comment_url" \
@@ -376,7 +376,7 @@ Enable debug logging for detailed troubleshooting:
376
376
 
377
377
  ```bash
378
378
  export AUTOPM_LOG_LEVEL=0 # Enable debug logging
379
- bash autopm/.claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER"
379
+ bash .claude/scripts/pm/issue-sync/preflight-validation.sh "$ISSUE_NUMBER"
380
380
  ```
381
381
 
382
382
  ## Migration from Legacy Issue Sync
@@ -0,0 +1,11 @@
1
+ ---
2
+ allowed-tools: Bash
3
+ ---
4
+
5
+ Run `node .claude/scripts/pm/what-next.js` using the Bash tool and show me the complete output.
6
+
7
+ - DO NOT truncate.
8
+ - DO NOT collapse.
9
+ - DO NOT abbreviate.
10
+ - Show ALL lines in full.
11
+ - DO NOT print any other comments.
@@ -96,7 +96,7 @@ autopm mcp add
96
96
  cp .claude/examples/mcp/context7.md .claude/mcp/
97
97
 
98
98
  # Or from installed framework
99
- cp /path/to/framework/autopm/.claude/examples/mcp/playwright-mcp.md .claude/mcp/
99
+ cp .claude/examples/mcp/playwright-mcp.md .claude/mcp/
100
100
  ```
101
101
 
102
102
  ### ✅ Enabling/Disabling Servers
@@ -8,8 +8,8 @@
8
8
  const path = require('path');
9
9
  const fs = require('fs');
10
10
  const chalk = require('chalk');
11
- const AzureDevOpsClient = require('../../lib/azure/client');
12
- const AzureFormatter = require('../../lib/azure/formatter');
11
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
12
+ const AzureFormatter = require('../../providers/azure/lib/formatter');
13
13
  const { table } = require('table');
14
14
 
15
15
  class AzureActiveWork {
@@ -18,22 +18,22 @@ const https = require('https');
18
18
  const yargs = require('yargs/yargs');
19
19
  const { hideBin } = require('yargs/helpers');
20
20
 
21
- // Import utilities
22
- const Logger = require('../../lib/utils/logger');
23
- const FileSystem = require('../../lib/utils/filesystem');
24
- const Config = require('../../lib/utils/config');
21
+ // TODO: Implement utility modules before using this script
22
+ // const Logger = require('../../lib/utils/logger');
23
+ // const FileSystem = require('../../lib/utils/filesystem');
24
+ // const Config = require('../../lib/utils/config');
25
25
 
26
26
  class AzureBlocked {
27
27
  constructor(options = {}) {
28
- // Initialize utilities
29
- const loggerOptions = {
30
- verbose: options.verbose || false,
31
- silent: options.silent || false
32
- };
33
-
34
- this.logger = new Logger(loggerOptions);
35
- this.fs = new FileSystem(this.logger);
36
- this.config = new Config(this.logger);
28
+ // Initialize utilities (commented out until utils are implemented)
29
+ // const loggerOptions = {
30
+ // verbose: options.verbose || false,
31
+ // silent: options.silent || false
32
+ // };
33
+
34
+ // this.logger = new Logger(loggerOptions);
35
+ // this.fs = new FileSystem(this.logger);
36
+ // this.config = new Config(this.logger);
37
37
 
38
38
  // Set options
39
39
  this.options = {
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs').promises;
11
11
  const chalk = require('chalk');
12
12
  const dotenv = require('dotenv');
13
- const AzureDevOpsClient = require('../../lib/azure/client');
13
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
14
14
 
15
15
  class AzureDaily {
16
16
  constructor(options = {}) {
@@ -7,7 +7,7 @@
7
7
 
8
8
  const path = require('path');
9
9
  const fs = require('fs');
10
- const AzureDevOpsClient = require('../../lib/azure/client');
10
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
11
11
 
12
12
  // Simple chalk replacement for stub
13
13
  const chalk = {
@@ -5,8 +5,8 @@
5
5
  * Lists all features in the current project
6
6
  */
7
7
 
8
- const AzureDevOpsClient = require('../../lib/azure/client');
9
- const AzureFormatter = require('../../lib/azure/formatter');
8
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
9
+ const AzureFormatter = require('../../providers/azure/lib/formatter');
10
10
  const chalk = require('chalk');
11
11
  const path = require('path');
12
12
  const fs = require('fs');
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs').promises;
11
11
  const chalk = require('chalk');
12
12
  const dotenv = require('dotenv');
13
- const AzureDevOpsClient = require('../../lib/azure/client');
13
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
14
14
 
15
15
  class AzureFeatureStatus {
16
16
  constructor(options = {}) {
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs').promises;
11
11
  const chalk = require('chalk');
12
12
  const dotenv = require('dotenv');
13
- const AzureDevOpsClient = require('../../lib/azure/client');
13
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
14
14
 
15
15
  class AzureNextTask {
16
16
  constructor(options = {}) {
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs').promises;
11
11
  const chalk = require('chalk');
12
12
  const dotenv = require('dotenv');
13
- const AzureDevOpsClient = require('../../lib/azure/client');
13
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
14
14
 
15
15
  class AzureSearch {
16
16
  constructor(options = {}) {
@@ -22,24 +22,24 @@ const yargs = require('yargs/yargs');
22
22
  const { hideBin } = require('yargs/helpers');
23
23
  const yaml = require('js-yaml');
24
24
 
25
- // Import utilities
26
- const Logger = require('../../lib/utils/logger');
27
- const FileSystem = require('../../lib/utils/filesystem');
28
- const Prompts = require('../../lib/utils/prompts');
29
- const Config = require('../../lib/utils/config');
25
+ // TODO: Implement utility modules before using this script
26
+ // const Logger = require('../../lib/utils/logger');
27
+ // const FileSystem = require('../../lib/utils/filesystem');
28
+ // const Prompts = require('../../lib/utils/prompts');
29
+ // const Config = require('../../lib/utils/config');
30
30
 
31
31
  class AzureSetup {
32
32
  constructor(options = {}) {
33
- // Initialize utilities
34
- const loggerOptions = {
35
- verbose: options.verbose || false,
36
- silent: options.silent || false
37
- };
38
-
39
- this.logger = new Logger(loggerOptions);
40
- this.fs = new FileSystem(this.logger);
41
- this.prompts = new Prompts(this.logger);
42
- this.config = new Config(this.logger);
33
+ // Initialize utilities (commented out until utils are implemented)
34
+ // const loggerOptions = {
35
+ // verbose: options.verbose || false,
36
+ // silent: options.silent || false
37
+ // };
38
+
39
+ // this.logger = new Logger(loggerOptions);
40
+ // this.fs = new FileSystem(this.logger);
41
+ // this.prompts = new Prompts(this.logger);
42
+ // this.config = new Config(this.logger);
43
43
 
44
44
  // Set options
45
45
  this.options = {
@@ -9,8 +9,8 @@
9
9
  const path = require('path');
10
10
  const fs = require('fs');
11
11
  const chalk = require('chalk');
12
- const AzureDevOpsClient = require('../../lib/azure/client');
13
- const AzureFormatter = require('../../lib/azure/formatter');
12
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
13
+ const AzureFormatter = require('../../providers/azure/lib/formatter');
14
14
  const { table } = require('table');
15
15
 
16
16
  class AzureSprintReport {
@@ -7,7 +7,7 @@
7
7
 
8
8
  const path = require('path');
9
9
  const fs = require('fs').promises;
10
- const AzureDevOpsClient = require('../../lib/azure/client');
10
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
11
11
 
12
12
  // Simple chalk replacement for stub
13
13
  const chalk = {
@@ -9,7 +9,7 @@ const path = require('path');
9
9
  const fs = require('fs').promises;
10
10
  const chalk = require('chalk');
11
11
  const dotenv = require('dotenv');
12
- const AzureDevOpsClient = require('../../lib/azure/client');
12
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
13
13
 
14
14
  class AzureUserStoryList {
15
15
  constructor(options = {}) {
@@ -9,7 +9,7 @@ const path = require('path');
9
9
  const fs = require('fs').promises;
10
10
  const chalk = require('chalk');
11
11
  const dotenv = require('dotenv');
12
- const AzureDevOpsClient = require('../../lib/azure/client');
12
+ const AzureDevOpsClient = require('../../providers/azure/lib/client');
13
13
 
14
14
  class AzureUserStoryStatus {
15
15
  constructor(options = {}) {
@@ -18,22 +18,22 @@ const https = require('https');
18
18
  const yargs = require('yargs/yargs');
19
19
  const { hideBin } = require('yargs/helpers');
20
20
 
21
- // Import utilities
22
- const Logger = require('../../lib/utils/logger');
23
- const FileSystem = require('../../lib/utils/filesystem');
24
- const Config = require('../../lib/utils/config');
21
+ // TODO: Implement utility modules before using this script
22
+ // const Logger = require('../../lib/utils/logger');
23
+ // const FileSystem = require('../../lib/utils/filesystem');
24
+ // const Config = require('../../lib/utils/config');
25
25
 
26
26
  class AzureValidate {
27
27
  constructor(options = {}) {
28
- // Initialize utilities
29
- const loggerOptions = {
30
- verbose: options.verbose || false,
31
- silent: options.silent || false
32
- };
33
-
34
- this.logger = new Logger(loggerOptions);
35
- this.fs = new FileSystem(this.logger);
36
- this.config = new Config(this.logger);
28
+ // Initialize utilities (commented out until utils are implemented)
29
+ // const loggerOptions = {
30
+ // verbose: options.verbose || false,
31
+ // silent: options.silent || false
32
+ // };
33
+
34
+ // this.logger = new Logger(loggerOptions);
35
+ // this.fs = new FileSystem(this.logger);
36
+ // this.config = new Config(this.logger);
37
37
 
38
38
  // Set options
39
39
  this.options = {
@@ -9,6 +9,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
9
  source "${SCRIPT_DIR}/logging-utils.sh"
10
10
  source "${SCRIPT_DIR}/datetime-utils.sh"
11
11
 
12
+ # Escape special characters for use in sed pattern
13
+ sed_escape_pattern() {
14
+ local str="$1"
15
+ # Escape characters that have special meaning in sed patterns: . * [ ] ^ $ \ /
16
+ printf '%s\n' "$str" | sed 's/[]\/$*.^[]/\\&/g'
17
+ }
18
+
19
+ # Escape special characters for use in sed replacement
20
+ sed_escape_replacement() {
21
+ local str="$1"
22
+ # Escape backslashes first, then other special characters
23
+ # This prevents double-escaping issues
24
+ printf '%s\n' "$str" | sed 's/\\/\\\\/g; s/&/\\&/g; s/|/\\|/g'
25
+ }
26
+
12
27
  # Update or add a field in frontmatter
13
28
  update_frontmatter_field() {
14
29
  local file_path="$1"
@@ -25,18 +40,32 @@ update_frontmatter_field() {
25
40
  # Create backup
26
41
  cp "$file_path" "${file_path}.bak"
27
42
 
43
+ # Escape field name and value for safe use in sed
44
+ local escaped_field_name
45
+ local escaped_field_value
46
+ escaped_field_name=$(sed_escape_pattern "$field_name")
47
+ escaped_field_value=$(sed_escape_replacement "$field_value")
48
+
28
49
  # Check if field exists
29
50
  if grep -q "^${field_name}:" "$file_path"; then
30
- # Update existing field
31
- sed -i.tmp "/^${field_name}:/c\\${field_name}: ${field_value}" "$file_path"
51
+ # Update existing field - delete old line and insert new one
52
+ # This approach avoids sed replacement string escaping issues
53
+ grep -v "^${escaped_field_name}:" "$file_path" > "${file_path}.tmp"
54
+ {
55
+ head -1 "${file_path}.tmp" # First --- line
56
+ printf '%s: %s\n' "$field_name" "$field_value" # New field value
57
+ tail -n +2 "${file_path}.tmp" # Rest of file
58
+ } > "${file_path}.tmp2" && mv "${file_path}.tmp2" "$file_path"
32
59
  rm -f "${file_path}.tmp"
33
60
  log_debug "Updated existing field: $field_name"
34
61
  else
35
62
  # Add new field after the first line of frontmatter (after opening ---)
36
- awk -v field="${field_name}: ${field_value}" '
37
- /^---$/ && NR==1 { print; print field; next }
38
- { print }
39
- ' "$file_path" > "${file_path}.tmp" && mv "${file_path}.tmp" "$file_path"
63
+ # Write the new line directly to avoid variable expansion issues
64
+ {
65
+ head -1 "$file_path" # First --- line
66
+ printf '%s: %s\n' "$field_name" "$field_value" # New field (preserves all chars)
67
+ tail -n +2 "$file_path" # Rest of file
68
+ } > "${file_path}.tmp" && mv "${file_path}.tmp" "$file_path"
40
69
  log_debug "Added new field: $field_name"
41
70
  fi
42
71
 
@@ -60,8 +89,14 @@ get_frontmatter_field() {
60
89
  return 1
61
90
  fi
62
91
 
92
+ # Escape field name for safe use in patterns
93
+ local escaped_field_name
94
+ escaped_field_name=$(sed_escape_pattern "$field_name")
95
+
63
96
  local field_value
64
- field_value=$(grep "^${field_name}:" "$file_path" | sed "s/^${field_name}: *//" | head -1)
97
+ # Use | delimiter to avoid conflicts with / in values
98
+ # Only remove the field name, colon, and exactly one space (YAML format)
99
+ field_value=$(grep "^${field_name}:" "$file_path" | sed "s|^${escaped_field_name}: ||" | head -1)
65
100
 
66
101
  log_debug "Retrieved field $field_name: '$field_value'"
67
102
  log_function_exit "get_frontmatter_field"
@@ -4,22 +4,26 @@
4
4
 
5
5
  set -euo pipefail
6
6
 
7
- # Colors for output
8
- readonly RED='\033[0;31m'
9
- readonly GREEN='\033[0;32m'
10
- readonly YELLOW='\033[1;33m'
11
- readonly BLUE='\033[0;34m'
12
- readonly PURPLE='\033[0;35m'
13
- readonly CYAN='\033[0;36m'
14
- readonly WHITE='\033[1;37m'
15
- readonly NC='\033[0m' # No Color
16
-
17
- # Log levels
18
- readonly LOG_LEVEL_DEBUG=0
19
- readonly LOG_LEVEL_INFO=1
20
- readonly LOG_LEVEL_WARNING=2
21
- readonly LOG_LEVEL_ERROR=3
22
- readonly LOG_LEVEL_SUCCESS=4
7
+ # Colors for output (only define if not already defined)
8
+ if [[ -z "${RED:-}" ]]; then
9
+ readonly RED='\033[0;31m'
10
+ readonly GREEN='\033[0;32m'
11
+ readonly YELLOW='\033[1;33m'
12
+ readonly BLUE='\033[0;34m'
13
+ readonly PURPLE='\033[0;35m'
14
+ readonly CYAN='\033[0;36m'
15
+ readonly WHITE='\033[1;37m'
16
+ readonly NC='\033[0m' # No Color
17
+ fi
18
+
19
+ # Log levels (only define if not already defined)
20
+ if [[ -z "${LOG_LEVEL_DEBUG:-}" ]]; then
21
+ readonly LOG_LEVEL_DEBUG=0
22
+ readonly LOG_LEVEL_INFO=1
23
+ readonly LOG_LEVEL_WARNING=2
24
+ readonly LOG_LEVEL_ERROR=3
25
+ readonly LOG_LEVEL_SUCCESS=4
26
+ fi
23
27
 
24
28
  # Default log level (can be overridden by AUTOPM_LOG_LEVEL env var)
25
29
  LOG_LEVEL=${AUTOPM_LOG_LEVEL:-$LOG_LEVEL_INFO}
@@ -12,7 +12,7 @@ source "${SCRIPT_DIR}/logging-utils.sh"
12
12
  validate_required_commands() {
13
13
  local commands=("$@")
14
14
 
15
- log_function_entry "validate_required_commands" "${commands[@]}"
15
+ log_function_entry "validate_required_commands" "${commands[@]:-}"
16
16
 
17
17
  local missing_commands=()
18
18