claude-autopm 1.25.0 → 1.27.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 (40) hide show
  1. package/README.md +111 -0
  2. package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -18
  3. package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -18
  4. package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -18
  5. package/autopm/.claude/agents/frameworks/react-ui-expert.md +1 -18
  6. package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +1 -18
  7. package/autopm/.claude/agents/frameworks/ux-design-expert.md +1 -18
  8. package/autopm/.claude/agents/languages/bash-scripting-expert.md +1 -18
  9. package/autopm/.claude/agents/languages/javascript-frontend-engineer.md +1 -18
  10. package/autopm/.claude/agents/languages/nodejs-backend-engineer.md +1 -18
  11. package/autopm/.claude/agents/languages/python-backend-engineer.md +1 -18
  12. package/autopm/.claude/agents/languages/python-backend-expert.md +1 -18
  13. package/autopm/.claude/commands/pm/epic-decompose.md +19 -5
  14. package/autopm/.claude/commands/pm/prd-new.md +14 -1
  15. package/autopm/.claude/includes/task-creation-excellence.md +18 -0
  16. package/autopm/.claude/lib/ai-task-generator.js +84 -0
  17. package/autopm/.claude/lib/cli-parser.js +148 -0
  18. package/autopm/.claude/lib/commands/pm/epicStatus.js +263 -0
  19. package/autopm/.claude/lib/dependency-analyzer.js +157 -0
  20. package/autopm/.claude/lib/frontmatter.js +224 -0
  21. package/autopm/.claude/lib/task-utils.js +64 -0
  22. package/autopm/.claude/scripts/pm-epic-decompose-local.js +158 -0
  23. package/autopm/.claude/scripts/pm-epic-list-local.js +103 -0
  24. package/autopm/.claude/scripts/pm-epic-show-local.js +70 -0
  25. package/autopm/.claude/scripts/pm-epic-update-local.js +56 -0
  26. package/autopm/.claude/scripts/pm-prd-list-local.js +111 -0
  27. package/autopm/.claude/scripts/pm-prd-new-local.js +196 -0
  28. package/autopm/.claude/scripts/pm-prd-parse-local.js +360 -0
  29. package/autopm/.claude/scripts/pm-prd-show-local.js +101 -0
  30. package/autopm/.claude/scripts/pm-prd-update-local.js +153 -0
  31. package/autopm/.claude/scripts/pm-sync-download-local.js +424 -0
  32. package/autopm/.claude/scripts/pm-sync-upload-local.js +473 -0
  33. package/autopm/.claude/scripts/pm-task-list-local.js +86 -0
  34. package/autopm/.claude/scripts/pm-task-show-local.js +92 -0
  35. package/autopm/.claude/scripts/pm-task-update-local.js +109 -0
  36. package/autopm/.claude/scripts/setup-local-mode.js +127 -0
  37. package/package.json +5 -3
  38. package/scripts/create-task-issues.sh +26 -0
  39. package/scripts/fix-invalid-command-refs.sh +4 -3
  40. package/scripts/fix-invalid-refs-simple.sh +8 -3
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Update Local Task
3
+ *
4
+ * Updates task frontmatter and automatically updates epic counters.
5
+ * Optionally validates dependency constraints.
6
+ *
7
+ * Usage:
8
+ * const { updateLocalTask } = require('./pm-task-update-local');
9
+ *
10
+ * // Update task status
11
+ * await updateLocalTask('epic-001', 'task-001', { status: 'completed' });
12
+ *
13
+ * // Update with dependency validation
14
+ * await updateLocalTask('epic-001', 'task-002', {
15
+ * status: 'completed',
16
+ * validateDependencies: true
17
+ * });
18
+ */
19
+
20
+ const fs = require('fs').promises;
21
+ const path = require('path');
22
+ const { parseFrontmatter, stringifyFrontmatter } = require('../lib/frontmatter');
23
+ const { showLocalTask } = require('./pm-task-show-local');
24
+ const { listLocalTasks } = require('./pm-task-list-local');
25
+ const { updateLocalEpic } = require('./pm-epic-update-local');
26
+
27
+ /**
28
+ * Update task frontmatter
29
+ *
30
+ * @param {string} epicId - Epic ID containing the task
31
+ * @param {string} taskId - Task ID to update
32
+ * @param {Object} updates - Fields to update
33
+ * @param {boolean} [updates.validateDependencies] - Validate dependencies before update
34
+ * @returns {Promise<Object>} Updated task object
35
+ * @throws {Error} If task not found or dependencies not met
36
+ */
37
+ async function updateLocalTask(epicId, taskId, updates) {
38
+ const { validateDependencies, ...frontmatterUpdates } = updates;
39
+
40
+ // Get current task
41
+ const task = await showLocalTask(epicId, taskId);
42
+
43
+ // Validate dependencies if requested and status is changing to completed
44
+ if (validateDependencies && frontmatterUpdates.status === 'completed') {
45
+ const dependencies = task.frontmatter.dependencies || [];
46
+
47
+ if (dependencies.length > 0) {
48
+ // Check if all dependencies are completed
49
+ const allTasks = await listLocalTasks(epicId);
50
+
51
+ for (const depId of dependencies) {
52
+ const depTask = allTasks.find(t =>
53
+ t.id === depId ||
54
+ t.id === `task-${epicId}-${depId.replace('task-', '')}` ||
55
+ t.filename === `${depId}.md`
56
+ );
57
+
58
+ if (depTask && depTask.status !== 'completed') {
59
+ throw new Error(
60
+ `Dependencies not met: ${depId} (status: ${depTask.status}) must be completed first`
61
+ );
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ // Track if status changed to/from completed
68
+ const oldStatus = task.frontmatter.status;
69
+ const newStatus = frontmatterUpdates.status || oldStatus;
70
+ const statusChanged = oldStatus !== newStatus;
71
+ const wasCompleted = oldStatus === 'completed';
72
+ const nowCompleted = newStatus === 'completed';
73
+
74
+ // Merge updates into frontmatter
75
+ const updatedFrontmatter = {
76
+ ...task.frontmatter,
77
+ ...frontmatterUpdates
78
+ };
79
+
80
+ // Preserve body content
81
+ const updatedContent = stringifyFrontmatter(updatedFrontmatter, task.body);
82
+
83
+ // Write updated content back to file
84
+ await fs.writeFile(task.path, updatedContent, 'utf8');
85
+
86
+ // Update epic tasks_completed counter if status changed
87
+ if (statusChanged && (wasCompleted || nowCompleted)) {
88
+ const allTasks = await listLocalTasks(epicId);
89
+ const completedCount = allTasks.filter(t => {
90
+ if (t.id === task.frontmatter.id || t.id === updatedFrontmatter.id) {
91
+ return nowCompleted;
92
+ }
93
+ return t.status === 'completed';
94
+ }).length;
95
+
96
+ await updateLocalEpic(epicId, {
97
+ tasks_completed: completedCount
98
+ });
99
+ }
100
+
101
+ return {
102
+ taskId,
103
+ epicId,
104
+ frontmatter: updatedFrontmatter,
105
+ body: task.body
106
+ };
107
+ }
108
+
109
+ module.exports = { updateLocalTask };
@@ -0,0 +1,127 @@
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Setup local mode directory structure
6
+ * Creates .claude/prds/, .claude/epics/, .claude/context/, .claude/logs/
7
+ *
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function setupLocalDirectories() {
11
+ const baseDir = path.join(process.cwd(), '.claude');
12
+
13
+ const directories = [
14
+ 'prds', // Product Requirements Documents
15
+ 'epics', // Epic definitions and task breakdowns
16
+ 'context', // Project context files (NEW)
17
+ 'logs' // Verification and operation logs (NEW)
18
+ ];
19
+
20
+ for (const dir of directories) {
21
+ const dirPath = path.join(baseDir, dir);
22
+
23
+ try {
24
+ await fs.mkdir(dirPath, { recursive: true, mode: 0o755 });
25
+ console.log(`✅ Created ${dirPath}`);
26
+ } catch (err) {
27
+ // EEXIST is OK - directory already exists
28
+ if (err.code !== 'EEXIST') {
29
+ throw err;
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Update .gitignore with ClaudeAutoPM local mode entries
37
+ * Creates .gitignore if it doesn't exist
38
+ * Appends entries if .gitignore exists (idempotent)
39
+ *
40
+ * @returns {Promise<void>}
41
+ */
42
+ async function updateGitignore() {
43
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
44
+
45
+ const entries = [
46
+ '# ClaudeAutoPM Local Mode',
47
+ '.claude/logs/*.log',
48
+ '.claude/context/.context-version',
49
+ '.claude/prds/drafts/',
50
+ ''
51
+ ].join('\n');
52
+
53
+ try {
54
+ // Try to read existing .gitignore
55
+ const existing = await fs.readFile(gitignorePath, 'utf8');
56
+
57
+ // Check if our entries are already present
58
+ if (!existing.includes('.claude/logs/')) {
59
+ // Append our entries
60
+ await fs.appendFile(gitignorePath, '\n' + entries);
61
+ console.log('✅ Updated .gitignore');
62
+ } else {
63
+ console.log('ℹ️ .gitignore already contains ClaudeAutoPM entries');
64
+ }
65
+ } catch (err) {
66
+ if (err.code === 'ENOENT') {
67
+ // .gitignore doesn't exist, create it
68
+ await fs.writeFile(gitignorePath, entries);
69
+ console.log('✅ Created .gitignore');
70
+ } else {
71
+ throw err;
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Main setup function
78
+ * Called during `autopm install` or standalone
79
+ *
80
+ * @returns {Promise<void>}
81
+ */
82
+ async function setup() {
83
+ console.log('🚀 Setting up ClaudeAutoPM local mode...\n');
84
+
85
+ try {
86
+ await setupLocalDirectories();
87
+ await updateGitignore();
88
+
89
+ console.log('\n✅ Local mode setup complete!');
90
+ console.log('\nCreated directories:');
91
+ console.log(' - .claude/prds/ (Product Requirements Documents)');
92
+ console.log(' - .claude/epics/ (Epic breakdowns and tasks)');
93
+ console.log(' - .claude/context/ (Project context files)');
94
+ console.log(' - .claude/logs/ (Operation logs)');
95
+ console.log('\nUpdated .gitignore with exclusions for:');
96
+ console.log(' - .claude/logs/*.log');
97
+ console.log(' - .claude/context/.context-version');
98
+ console.log(' - .claude/prds/drafts/');
99
+ console.log('\nYou can now use local mode commands:');
100
+ console.log(' /pm:prd-new --local "Feature Name"');
101
+ console.log(' /pm:epic-decompose --local <id>');
102
+ console.log(' /pm:context-create');
103
+
104
+ } catch (error) {
105
+ console.error('❌ Setup failed:', error.message);
106
+
107
+ // Provide helpful error messages
108
+ if (error.code === 'EACCES') {
109
+ console.error('\nPermission denied. Try running with sudo or check directory permissions.');
110
+ } else if (error.code === 'ENOSPC') {
111
+ console.error('\nNo space left on device. Free up some space and try again.');
112
+ }
113
+
114
+ process.exit(1);
115
+ }
116
+ }
117
+
118
+ // If run directly (not required as module)
119
+ if (require.main === module) {
120
+ setup();
121
+ }
122
+
123
+ module.exports = {
124
+ setupLocalDirectories,
125
+ updateGitignore,
126
+ setup
127
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "1.25.0",
3
+ "version": "1.27.0",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "bin": {
@@ -123,11 +123,13 @@
123
123
  "glob": "^11.0.3",
124
124
  "inquirer": "^12.9.6",
125
125
  "js-yaml": "^4.1.0",
126
+ "markdown-it": "^14.1.0",
126
127
  "moment": "^2.29.4",
127
128
  "ora": "^5.4.1",
128
129
  "simple-git": "^3.20.0",
129
130
  "table": "^6.8.1",
130
131
  "which": "^4.0.0",
132
+ "yaml": "^2.8.1",
131
133
  "yargs": "^17.7.2"
132
134
  },
133
135
  "devDependencies": {
@@ -149,8 +151,8 @@
149
151
  "sinon": "^21.0.0"
150
152
  },
151
153
  "optionalDependencies": {
152
- "@upstash/context7-mcp": "^1.0.0",
153
- "@playwright/mcp": "^0.0.40"
154
+ "@playwright/mcp": "^0.0.40",
155
+ "@upstash/context7-mcp": "^1.0.0"
154
156
  },
155
157
  "publishConfig": {
156
158
  "access": "public",
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ # Create GitHub issues for all tasks in epic
3
+
4
+ cd .claude/epics/ccpm-001-features-integration/
5
+
6
+ echo "Creating GitHub issues for all tasks..."
7
+ echo ""
8
+
9
+ for task_file in task-*.md; do
10
+ if [ "$task_file" != "task-*.md" ]; then
11
+ task_num=$(echo "$task_file" | grep -o '[0-9]\+' | head -1)
12
+ task_title=$(grep "^title:" "$task_file" | head -1 | sed 's/title: //')
13
+
14
+ echo "Creating issue for TASK-$task_num: $task_title"
15
+
16
+ issue_url=$(gh issue create \
17
+ --title "[TASK-$task_num] $task_title" \
18
+ --body-file "$task_file" \
19
+ --label "task" --label "enhancement")
20
+
21
+ echo " ✅ Created: $issue_url"
22
+ echo ""
23
+ fi
24
+ done
25
+
26
+ echo "✅ All task issues created!"
@@ -71,11 +71,12 @@ remove_from_file() {
71
71
  local cmd="$2"
72
72
 
73
73
  if grep -q "$cmd" "$file" 2>/dev/null; then
74
- # Remove lines containing the command
74
+ # Remove lines containing the command (with word boundary)
75
+ # Match only whole command tokens, not substrings
75
76
  if [[ "$OSTYPE" == "darwin"* ]]; then
76
- sed -i '' "/$cmd/d" "$file"
77
+ sed -i '' -E "/([[:space:]]|^)${cmd}([[:space:]]|\$|[[:punct:]])/d" "$file"
77
78
  else
78
- sed -i "/$cmd/d" "$file"
79
+ sed -i -E "/([[:space:]]|^)${cmd}([[:space:]]|\$|[[:punct:]])/d" "$file"
79
80
  fi
80
81
  echo " ✗ $file: removed $cmd"
81
82
  ((total_changes++))
@@ -13,19 +13,23 @@ safe_replace() {
13
13
  local old="$1"
14
14
  local new="$2"
15
15
  local desc="$3"
16
+ local count=0
16
17
 
17
18
  echo "Replacing: $old → $new ($desc)"
18
19
 
19
20
  # Find and replace (exclude our analysis docs)
20
- find . -name "*.md" -type f ! -path "*/MISSING-COMMANDS-ANALYSIS.md" ! -path "*/node_modules/*" -exec grep -l "$old" {} \; 2>/dev/null | while read file; do
21
+ while IFS= read -r file; do
21
22
  if [[ "$OSTYPE" == "darwin"* ]]; then
22
23
  sed -i '' "s|$old|$new|g" "$file"
23
24
  else
24
25
  sed -i "s|$old|$new|g" "$file"
25
26
  fi
26
27
  echo " ✓ $file"
27
- ((total++)) || true
28
- done
28
+ ((count++))
29
+ done < <(find . -name "*.md" -type f ! -path "*/MISSING-COMMANDS-ANALYSIS.md" ! -path "*/node_modules/*" -exec grep -l "$old" {} \; 2>/dev/null)
30
+
31
+ ((total += count))
32
+ echo " Updated: $count file(s)"
29
33
  }
30
34
 
31
35
  # Replacements
@@ -39,6 +43,7 @@ safe_replace "/pm:prd-split" "/pm:epic-split" "split after parse"
39
43
 
40
44
  echo ""
41
45
  echo "✅ Replacements complete!"
46
+ echo "📊 Total files updated: $total"
42
47
  echo ""
43
48
  echo "📝 Note: The following invalid commands still exist in docs:"
44
49
  echo " - Azure commands (/pm:azure-*)"