claude-autopm 1.18.0 → 1.20.1

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 (108) hide show
  1. package/README.md +159 -0
  2. package/autopm/.claude/agents/README.md +1 -1
  3. package/autopm/.claude/agents/core/mcp-manager.md +1 -1
  4. package/autopm/.claude/agents/decision-matrices/python-backend-selection.md +25 -25
  5. package/autopm/.claude/agents/decision-matrices/ui-framework-selection.md +43 -43
  6. package/autopm/.claude/agents/devops/github-operations-specialist.md +1 -1
  7. package/autopm/.claude/agents/frameworks/README.md +5 -5
  8. package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -1
  9. package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -1
  10. package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -1
  11. package/autopm/.claude/agents/frameworks/react-ui-expert.md +3 -3
  12. package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +3 -3
  13. package/autopm/.claude/agents/frameworks/ux-design-expert.md +3 -3
  14. package/autopm/.claude/commands/infrastructure/traefik-setup.md +1 -1
  15. package/autopm/.claude/commands/playwright/test-scaffold.md +1 -1
  16. package/autopm/.claude/commands/pm/context.md +11 -0
  17. package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
  18. package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
  19. package/autopm/.claude/commands/pm/epic-start.md +19 -0
  20. package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
  21. package/autopm/.claude/commands/pm/epic-sync.md +14 -14
  22. package/autopm/.claude/commands/pm/issue-start.md +50 -5
  23. package/autopm/.claude/commands/pm/issue-sync.md +15 -15
  24. package/autopm/.claude/commands/pm/what-next.md +11 -0
  25. package/autopm/.claude/commands/ui/bootstrap-scaffold.md +6 -5
  26. package/autopm/.claude/commands/ui/tailwind-system.md +1 -1
  27. package/autopm/.claude/examples/mcp/playwright-mcp.md +2 -2
  28. package/autopm/.claude/examples/mcp-servers.example.json +2 -2
  29. package/autopm/.claude/hooks/docker-first-enforcement.sh +1 -1
  30. package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
  31. package/autopm/.claude/mcp/playwright-mcp.md +2 -2
  32. package/autopm/.claude/rules/agent-coordination.md +26 -24
  33. package/autopm/.claude/rules/docker-first-development.md +1 -1
  34. package/autopm/.claude/rules/infrastructure-pipeline.md +1 -1
  35. package/autopm/.claude/rules/ui-development-standards.md +1 -1
  36. package/autopm/.claude/rules/visual-testing.md +3 -3
  37. package/autopm/.claude/scripts/azure/active-work.js +2 -2
  38. package/autopm/.claude/scripts/azure/blocked.js +13 -13
  39. package/autopm/.claude/scripts/azure/daily.js +1 -1
  40. package/autopm/.claude/scripts/azure/dashboard.js +1 -1
  41. package/autopm/.claude/scripts/azure/feature-list.js +2 -2
  42. package/autopm/.claude/scripts/azure/feature-status.js +1 -1
  43. package/autopm/.claude/scripts/azure/next-task.js +1 -1
  44. package/autopm/.claude/scripts/azure/search.js +1 -1
  45. package/autopm/.claude/scripts/azure/setup.js +15 -15
  46. package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
  47. package/autopm/.claude/scripts/azure/sync.js +1 -1
  48. package/autopm/.claude/scripts/azure/us-list.js +1 -1
  49. package/autopm/.claude/scripts/azure/us-status.js +1 -1
  50. package/autopm/.claude/scripts/azure/validate.js +13 -13
  51. package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
  52. package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
  53. package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
  54. package/autopm/.claude/scripts/pm/context.js +338 -0
  55. package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
  56. package/autopm/.claude/scripts/pm/lib/README.md +85 -0
  57. package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
  58. package/autopm/.claude/scripts/pm/next.js +25 -1
  59. package/autopm/.claude/scripts/pm/what-next.js +660 -0
  60. package/autopm/.claude/teams.json +3 -5
  61. package/autopm/.claude/templates/claude-templates/addons/devops-agents.md +2 -2
  62. package/autopm/.claude/templates/claude-templates/addons/docker-agents.md +4 -4
  63. package/autopm/.claude/templates/claude-templates/addons/minimal-agents.md +1 -1
  64. package/autopm/.claude/templates/issue-decomposition/api.yaml +2 -2
  65. package/autopm/.claude/templates/issue-decomposition/auth.yaml +4 -4
  66. package/autopm/.claude/templates/issue-decomposition/crud.yaml +3 -3
  67. package/autopm/.claude/templates/issue-decomposition/default.yaml +1 -1
  68. package/autopm/.claude/templates/issue-decomposition/ui-feature.yaml +2 -2
  69. package/bin/autopm.js +25 -0
  70. package/package.json +1 -2
  71. package/lib/agentExecutor.js.deprecated +0 -101
  72. package/lib/azure/cache.js +0 -80
  73. package/lib/azure/client.js +0 -77
  74. package/lib/azure/formatter.js +0 -177
  75. package/lib/commandHelpers.js +0 -177
  76. package/lib/context/manager.js +0 -290
  77. package/lib/documentation/manager.js +0 -528
  78. package/lib/github/workflow-manager.js +0 -546
  79. package/lib/helpers/azure-batch-api.js +0 -133
  80. package/lib/helpers/azure-cache-manager.js +0 -287
  81. package/lib/helpers/azure-parallel-processor.js +0 -158
  82. package/lib/helpers/azure-work-item-create.js +0 -278
  83. package/lib/helpers/gh-issue-create.js +0 -250
  84. package/lib/helpers/interactive-prompt.js +0 -336
  85. package/lib/helpers/output-manager.js +0 -335
  86. package/lib/helpers/progress-indicator.js +0 -258
  87. package/lib/performance/benchmarker.js +0 -429
  88. package/lib/pm/epic-decomposer.js +0 -273
  89. package/lib/pm/epic-syncer.js +0 -221
  90. package/lib/prdMetadata.js +0 -270
  91. package/lib/providers/azure/index.js +0 -234
  92. package/lib/providers/factory.js +0 -87
  93. package/lib/providers/github/index.js +0 -204
  94. package/lib/providers/interface.js +0 -73
  95. package/lib/python/scaffold-manager.js +0 -576
  96. package/lib/react/scaffold-manager.js +0 -745
  97. package/lib/regression/analyzer.js +0 -578
  98. package/lib/release/manager.js +0 -324
  99. package/lib/tailwind/manager.js +0 -486
  100. package/lib/traefik/manager.js +0 -484
  101. package/lib/utils/colors.js +0 -126
  102. package/lib/utils/config.js +0 -317
  103. package/lib/utils/filesystem.js +0 -316
  104. package/lib/utils/logger.js +0 -135
  105. package/lib/utils/prompts.js +0 -294
  106. package/lib/utils/shell.js +0 -237
  107. package/lib/validators/email-validator.js +0 -337
  108. package/lib/workflow/manager.js +0 -449
@@ -0,0 +1,338 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { logError } = require('./lib/logger');
4
+
5
+ /**
6
+ * PM Context Script
7
+ * Displays current project context, configuration, and progress
8
+ */
9
+
10
+ // Constants
11
+ const TASK_FILE_PATTERN = /^\d{3}\.md$/;
12
+
13
+ async function context() {
14
+ console.log('');
15
+ console.log('🎯 Project Context');
16
+ console.log('='.repeat(60));
17
+ console.log('');
18
+
19
+ // Project Information
20
+ console.log('📦 Project Information:');
21
+
22
+ // Get project name from package.json or directory name
23
+ let projectName = path.basename(process.cwd());
24
+ try {
25
+ if (fs.existsSync('package.json')) {
26
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
27
+ if (pkg.name) {
28
+ projectName = pkg.name;
29
+ }
30
+ }
31
+ } catch (err) {
32
+ // Use directory name as fallback
33
+ }
34
+ console.log(` Name: ${projectName}`);
35
+ console.log(` Directory: ${process.cwd()}`);
36
+ console.log('');
37
+
38
+ // Configuration
39
+ console.log('⚙️ Configuration:');
40
+ let provider = 'Not configured';
41
+ let githubOwner = '-';
42
+ let githubRepo = '-';
43
+ let azureOrg = '-';
44
+ let azureProject = '-';
45
+
46
+ try {
47
+ if (fs.existsSync('.claude/config.json')) {
48
+ const config = JSON.parse(fs.readFileSync('.claude/config.json', 'utf8'));
49
+ if (config.provider) {
50
+ provider = config.provider.charAt(0).toUpperCase() + config.provider.slice(1);
51
+ }
52
+ if (config.github) {
53
+ githubOwner = config.github.owner || '-';
54
+ githubRepo = config.github.repo || '-';
55
+ }
56
+ if (config.azure) {
57
+ azureOrg = config.azure.organization || '-';
58
+ azureProject = config.azure.project || '-';
59
+ }
60
+ }
61
+ } catch (err) {
62
+ // Config not found
63
+ }
64
+
65
+ console.log(` Provider: ${provider}`);
66
+ if (provider.toLowerCase() === 'github') {
67
+ console.log(` GitHub Owner: ${githubOwner}`);
68
+ console.log(` GitHub Repo: ${githubRepo}`);
69
+ } else if (provider.toLowerCase() === 'azure') {
70
+ console.log(` Azure Org: ${azureOrg}`);
71
+ console.log(` Azure Project: ${azureProject}`);
72
+ }
73
+ console.log('');
74
+
75
+ // Active Team
76
+ console.log('👥 Active Team:');
77
+ let activeTeam = 'Default';
78
+ try {
79
+ if (fs.existsSync('.claude/active_team.txt')) {
80
+ activeTeam = fs.readFileSync('.claude/active_team.txt', 'utf8').trim();
81
+ }
82
+ } catch (err) {
83
+ // No active team
84
+ }
85
+ console.log(` Team: ${activeTeam}`);
86
+ console.log('');
87
+
88
+ // PRDs
89
+ console.log('📄 Product Requirements (PRDs):');
90
+ let prdCount = 0;
91
+ let prdNames = [];
92
+ try {
93
+ if (fs.existsSync('.claude/prds')) {
94
+ const files = fs.readdirSync('.claude/prds')
95
+ .filter(f => f.endsWith('.md') && !f.startsWith('.'));
96
+ prdCount = files.length;
97
+ prdNames = files.map(f => f.replace('.md', ''));
98
+ }
99
+ } catch (err) {
100
+ // No PRDs
101
+ }
102
+
103
+ console.log(` Total: ${prdCount}`);
104
+ if (prdCount > 0) {
105
+ prdNames.slice(0, 5).forEach(name => {
106
+ console.log(` • ${name}`);
107
+ });
108
+ if (prdCount > 5) {
109
+ console.log(` ... and ${prdCount - 5} more`);
110
+ }
111
+ }
112
+ console.log('');
113
+
114
+ // Epics with Progress
115
+ console.log('📚 Epics & Progress:');
116
+ let epicCount = 0;
117
+ let totalTasks = 0;
118
+ let completedTasks = 0;
119
+ let inProgressTasks = 0;
120
+ let pendingTasks = 0;
121
+ let epicDetails = [];
122
+
123
+ try {
124
+ if (fs.existsSync('.claude/epics')) {
125
+ const epicDirs = fs.readdirSync('.claude/epics', { withFileTypes: true })
126
+ .filter(d => d.isDirectory())
127
+ .map(d => d.name);
128
+
129
+ epicCount = epicDirs.length;
130
+
131
+ // Helper to count tasks in a directory and update counters
132
+ function countTasksInDir(dir, counters) {
133
+ try {
134
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
135
+ for (const entry of entries) {
136
+ const fullPath = path.join(dir, entry.name);
137
+ if (entry.isDirectory()) {
138
+ countTasksInDir(fullPath, counters);
139
+ } else if (entry.isFile() && TASK_FILE_PATTERN.test(entry.name)) {
140
+ counters.epicTasks++;
141
+ totalTasks++;
142
+ try {
143
+ const content = fs.readFileSync(fullPath, 'utf8');
144
+ const statusMatch = content.match(/^status:\s*(.+)$/m);
145
+ if (statusMatch) {
146
+ const status = statusMatch[1].trim().toLowerCase();
147
+ if (status === 'completed' || status === 'done' || status === 'closed') {
148
+ counters.epicCompleted++;
149
+ completedTasks++;
150
+ } else if (status === 'in-progress' || status === 'in_progress') {
151
+ counters.epicInProgress++;
152
+ inProgressTasks++;
153
+ } else {
154
+ counters.epicPending++;
155
+ pendingTasks++;
156
+ }
157
+ } else {
158
+ counters.epicPending++;
159
+ pendingTasks++;
160
+ }
161
+ } catch (err) {
162
+ counters.epicPending++;
163
+ pendingTasks++;
164
+ }
165
+ }
166
+ }
167
+ } catch (err) {
168
+ // Directory read error
169
+ }
170
+ }
171
+
172
+ for (const epicDir of epicDirs) {
173
+ const epicPath = path.join('.claude/epics', epicDir);
174
+ // Count tasks in this epic (including subdirectories)
175
+ let counters = {
176
+ epicTasks: 0,
177
+ epicCompleted: 0,
178
+ epicInProgress: 0,
179
+ epicPending: 0
180
+ };
181
+ countTasksInDir(epicPath, counters);
182
+ if (counters.epicTasks > 0) {
183
+ const progress = Math.round((counters.epicCompleted / counters.epicTasks) * 100);
184
+ epicDetails.push({
185
+ name: epicDir,
186
+ tasks: counters.epicTasks,
187
+ completed: counters.epicCompleted,
188
+ inProgress: counters.epicInProgress,
189
+ pending: counters.epicPending,
190
+ progress: progress
191
+ });
192
+ }
193
+ }
194
+ }
195
+ } catch (err) {
196
+ // No epics
197
+ }
198
+
199
+ console.log(` Total Epics: ${epicCount}`);
200
+ console.log(` Total Tasks: ${totalTasks}`);
201
+ console.log(` Completed: ${completedTasks}`);
202
+ console.log(` In Progress: ${inProgressTasks}`);
203
+ console.log(` Pending: ${pendingTasks}`);
204
+ console.log('');
205
+
206
+ if (epicDetails.length > 0) {
207
+ console.log(' Epic Breakdown:');
208
+ epicDetails.slice(0, 5).forEach(epic => {
209
+ const progressBar = createProgressBar(epic.progress, 20);
210
+ console.log(` ${epic.name}`);
211
+ console.log(` ${progressBar} ${epic.progress}% (${epic.completed}/${epic.tasks} tasks)`);
212
+ });
213
+ if (epicDetails.length > 5) {
214
+ console.log(` ... and ${epicDetails.length - 5} more epics`);
215
+ }
216
+ console.log('');
217
+ }
218
+
219
+ // Overall Progress
220
+ if (totalTasks > 0) {
221
+ const overallProgress = Math.round((completedTasks / totalTasks) * 100);
222
+ console.log('📊 Overall Progress:');
223
+ const overallBar = createProgressBar(overallProgress, 40);
224
+ console.log(` ${overallBar} ${overallProgress}%`);
225
+ console.log(` ${completedTasks} / ${totalTasks} tasks completed`);
226
+ console.log('');
227
+ }
228
+
229
+ // Current/Recent Activity
230
+ console.log('🔄 Recent Activity:');
231
+ try {
232
+ // Find most recently modified task file
233
+ let recentTask = null;
234
+ let recentTime = 0;
235
+
236
+ if (fs.existsSync('.claude/epics')) {
237
+ function findRecentTask(dir) {
238
+ let bestTask = null;
239
+ let bestTime = 0;
240
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
241
+
242
+ for (const entry of entries) {
243
+ const fullPath = path.join(dir, entry.name);
244
+
245
+ if (entry.isDirectory()) {
246
+ const subTask = findRecentTask(fullPath);
247
+ if (subTask && subTask.timeMs > bestTime) {
248
+ bestTask = subTask;
249
+ bestTime = subTask.timeMs;
250
+ }
251
+ } else if (entry.isFile() && TASK_FILE_PATTERN.test(entry.name)) {
252
+ const stats = fs.statSync(fullPath);
253
+ if (stats.mtimeMs > bestTime) {
254
+ const content = fs.readFileSync(fullPath, 'utf8');
255
+ const titleMatch = content.match(/^#\s+(.+)$/m);
256
+ const statusMatch = content.match(/^status:\s*(.+)$/m);
257
+ bestTask = {
258
+ path: fullPath,
259
+ title: titleMatch ? titleMatch[1] : entry.name,
260
+ status: statusMatch ? statusMatch[1].trim() : 'pending',
261
+ time: stats.mtime,
262
+ timeMs: stats.mtimeMs
263
+ };
264
+ bestTime = stats.mtimeMs;
265
+ }
266
+ }
267
+ }
268
+ return bestTask;
269
+ }
270
+
271
+ const foundTask = findRecentTask('.claude/epics');
272
+ if (foundTask) {
273
+ recentTask = foundTask;
274
+ recentTime = foundTask.timeMs;
275
+ }
276
+ }
277
+
278
+ if (recentTask) {
279
+ const timeAgo = getTimeAgo(recentTask.time);
280
+ console.log(` Last Modified: ${recentTask.title}`);
281
+ console.log(` Status: ${recentTask.status}`);
282
+ console.log(` Modified: ${timeAgo}`);
283
+ console.log(` File: ${recentTask.path}`);
284
+ } else {
285
+ console.log(' No recent activity');
286
+ }
287
+ } catch (err) {
288
+ console.log(' No recent activity');
289
+ }
290
+
291
+ console.log('');
292
+ console.log('💡 Quick Commands:');
293
+ console.log(' /pm:next # Get next priority task');
294
+ console.log(' /pm:status # Detailed project status');
295
+ console.log(' /pm:standup # Generate standup report');
296
+ console.log(' /pm:search <keyword> # Search PRDs and epics');
297
+ console.log('');
298
+ }
299
+
300
+ // Helper function to create progress bar
301
+ function createProgressBar(percentage, length) {
302
+ const filled = Math.round((percentage / 100) * length);
303
+ const empty = length - filled;
304
+ return '[' + '='.repeat(filled) + '-'.repeat(empty) + ']';
305
+ }
306
+
307
+ // Helper function to get human-readable time ago
308
+ function getTimeAgo(date) {
309
+ const seconds = Math.floor((new Date() - date) / 1000);
310
+
311
+ const intervals = {
312
+ year: 31536000,
313
+ month: 2592000,
314
+ week: 604800,
315
+ day: 86400,
316
+ hour: 3600,
317
+ minute: 60
318
+ };
319
+
320
+ for (const [name, secondsInInterval] of Object.entries(intervals)) {
321
+ const interval = Math.floor(seconds / secondsInInterval);
322
+ if (interval >= 1) {
323
+ return `${interval} ${name}${interval > 1 ? 's' : ''} ago`;
324
+ }
325
+ }
326
+
327
+ return 'just now';
328
+ }
329
+
330
+ // Run if called directly
331
+ if (require.main === module) {
332
+ context().catch(err => {
333
+ logError('Error executing context command', err);
334
+ process.exit(1);
335
+ });
336
+ }
337
+
338
+ module.exports = context;
@@ -6,9 +6,9 @@ set -euo pipefail
6
6
 
7
7
  # Load libraries
8
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
- source "${SCRIPT_DIR}/../../lib/logging-utils.sh"
10
- source "${SCRIPT_DIR}/../../lib/frontmatter-utils.sh"
11
- source "${SCRIPT_DIR}/../../lib/datetime-utils.sh"
9
+ source "${SCRIPT_DIR}/../lib/logging-utils.sh"
10
+ source "${SCRIPT_DIR}/../lib/frontmatter-utils.sh"
11
+ source "${SCRIPT_DIR}/../lib/datetime-utils.sh"
12
12
 
13
13
  # Script configuration
14
14
  readonly ISSUE_NUMBER="${1:-}"
@@ -0,0 +1,85 @@
1
+ # PM Scripts Library
2
+
3
+ This directory contains shared utilities for PM command scripts.
4
+
5
+ ## Logger
6
+
7
+ The `logger.js` module provides consistent, colored logging across all PM commands.
8
+
9
+ ### Usage
10
+
11
+ ```javascript
12
+ const { logInfo, logSuccess, logWarning, logError, logDebug } = require('./lib/logger');
13
+
14
+ // Info message (blue ℹ icon)
15
+ logInfo('Processing task...');
16
+
17
+ // Success message (green ✓ icon)
18
+ logSuccess('Task completed successfully');
19
+
20
+ // Warning message (yellow ⚠ icon)
21
+ logWarning('Task may require attention');
22
+
23
+ // Error message (red ❌ icon)
24
+ logError('Failed to process task', error);
25
+ // The error parameter is optional and will display error.message and stack (if DEBUG=1)
26
+
27
+ // Debug message (only shown if DEBUG=1)
28
+ logDebug('Detailed debug information');
29
+ ```
30
+
31
+ ### Environment Variables
32
+
33
+ - `DEBUG=1` - Enable debug logging and full error stack traces
34
+
35
+ ### Color Constants
36
+
37
+ If you need custom formatting, you can import the colors:
38
+
39
+ ```javascript
40
+ const { colors } = require('./lib/logger');
41
+
42
+ console.log(`${colors.blue}Custom message${colors.reset}`);
43
+ ```
44
+
45
+ Available colors:
46
+ - `colors.red`
47
+ - `colors.green`
48
+ - `colors.yellow`
49
+ - `colors.blue`
50
+ - `colors.cyan`
51
+ - `colors.gray`
52
+ - `colors.reset`
53
+
54
+ ## Migration Guide
55
+
56
+ To migrate existing PM scripts to use the logger:
57
+
58
+ 1. Import the logger at the top of your file:
59
+ ```javascript
60
+ const { logError, logWarning, logInfo, logSuccess } = require('./lib/logger');
61
+ ```
62
+
63
+ 2. Replace `console.error()` calls:
64
+ ```javascript
65
+ // Before
66
+ console.error('Error:', err.message);
67
+
68
+ // After
69
+ logError('Error description', err);
70
+ ```
71
+
72
+ 3. Add error handling to promise chains:
73
+ ```javascript
74
+ // Before
75
+ const results = await Promise.all(promises);
76
+
77
+ // After
78
+ const promises = items.map(item =>
79
+ processItem(item).catch(err => {
80
+ logWarning(`Failed to process ${item.name}`, err);
81
+ return defaultValue;
82
+ })
83
+ );
84
+ const results = await Promise.all(promises);
85
+ ```
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Logging utility for PM scripts
3
+ * Provides consistent colored output across all PM commands
4
+ */
5
+
6
+ // ANSI color codes
7
+ const colors = {
8
+ reset: '\x1b[0m',
9
+ red: '\x1b[31m',
10
+ green: '\x1b[32m',
11
+ yellow: '\x1b[33m',
12
+ blue: '\x1b[34m',
13
+ gray: '\x1b[90m',
14
+ cyan: '\x1b[36m'
15
+ };
16
+
17
+ /**
18
+ * Log an info message
19
+ * @param {string} message - The message to log
20
+ */
21
+ function logInfo(message) {
22
+ console.log(`${colors.blue}ℹ${colors.reset} ${message}`);
23
+ }
24
+
25
+ /**
26
+ * Log a success message
27
+ * @param {string} message - The message to log
28
+ */
29
+ function logSuccess(message) {
30
+ console.log(`${colors.green}✓${colors.reset} ${message}`);
31
+ }
32
+
33
+ /**
34
+ * Log a warning message
35
+ * @param {string} message - The message to log
36
+ */
37
+ function logWarning(message) {
38
+ console.warn(`${colors.yellow}⚠${colors.reset} ${message}`);
39
+ }
40
+
41
+ /**
42
+ * Log an error message
43
+ * @param {string} message - The message to log
44
+ * @param {Error} [error] - Optional error object for details
45
+ */
46
+ function logError(message, error) {
47
+ console.error(`${colors.red}❌${colors.reset} ${message}`);
48
+ if (error && error.message) {
49
+ console.error(`${colors.gray} ${error.message}${colors.reset}`);
50
+ }
51
+ if (error && error.stack && process.env.DEBUG) {
52
+ console.error(`${colors.gray}${error.stack}${colors.reset}`);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Log a debug message (only shown if DEBUG env var is set)
58
+ * @param {string} message - The message to log
59
+ */
60
+ function logDebug(message) {
61
+ if (process.env.DEBUG) {
62
+ console.log(`${colors.gray}[DEBUG]${colors.reset} ${message}`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Export colors for custom formatting
68
+ */
69
+ const logColors = colors;
70
+
71
+ module.exports = {
72
+ logInfo,
73
+ logSuccess,
74
+ logWarning,
75
+ logError,
76
+ logDebug,
77
+ colors: logColors
78
+ };
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { logError } = require('./lib/logger');
3
4
 
4
5
  /**
5
6
  * PM Next Script (Node.js version)
@@ -73,11 +74,34 @@ async function next() {
73
74
  }
74
75
 
75
76
  addMessage('');
77
+
78
+ // Display TDD reminder if tasks are available
79
+ if (result.found > 0) {
80
+ displayTddReminder(addMessage);
81
+ }
82
+
76
83
  addMessage(`📊 Summary: ${result.found} tasks ready to start`);
77
84
 
78
85
  return result;
79
86
  }
80
87
 
88
+ /**
89
+ * Display TDD reminder to ensure test-driven development practices
90
+ * Extracted to maintain single responsibility and improve testability
91
+ * @param {Function} addMessage - Function to add messages to the output
92
+ */
93
+ function displayTddReminder(addMessage) {
94
+ addMessage('⚠️ TDD REMINDER - Before starting work:');
95
+ addMessage('');
96
+ addMessage(' 🚨 ALWAYS follow Test-Driven Development:');
97
+ addMessage(' 1. RED: Write failing test first');
98
+ addMessage(' 2. GREEN: Write minimal code to pass');
99
+ addMessage(' 3. REFACTOR: Clean up while keeping tests green');
100
+ addMessage('');
101
+ addMessage(' See .claude/rules/tdd.enforcement.md for details');
102
+ addMessage('');
103
+ }
104
+
81
105
  // Helper function to find available tasks
82
106
  async function findAvailableTasks() {
83
107
  const availableTasks = [];
@@ -161,7 +185,7 @@ if (require.main === module) {
161
185
  module.exports.next().then(() => {
162
186
  process.exit(0);
163
187
  }).catch(err => {
164
- console.error('Next tasks failed:', err.message);
188
+ logError('Next tasks command failed', err);
165
189
  process.exit(1);
166
190
  });
167
191
  }