claude-autopm 1.26.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.
- package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -18
- package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -18
- package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -18
- package/autopm/.claude/agents/frameworks/react-ui-expert.md +1 -18
- package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +1 -18
- package/autopm/.claude/agents/frameworks/ux-design-expert.md +1 -18
- package/autopm/.claude/agents/languages/bash-scripting-expert.md +1 -18
- package/autopm/.claude/agents/languages/javascript-frontend-engineer.md +1 -18
- package/autopm/.claude/agents/languages/nodejs-backend-engineer.md +1 -18
- package/autopm/.claude/agents/languages/python-backend-engineer.md +1 -18
- package/autopm/.claude/agents/languages/python-backend-expert.md +1 -18
- package/autopm/.claude/commands/pm/epic-decompose.md +19 -5
- package/autopm/.claude/commands/pm/prd-new.md +14 -1
- package/autopm/.claude/includes/task-creation-excellence.md +18 -0
- package/autopm/.claude/lib/ai-task-generator.js +84 -0
- package/autopm/.claude/lib/cli-parser.js +148 -0
- package/autopm/.claude/lib/dependency-analyzer.js +157 -0
- package/autopm/.claude/lib/frontmatter.js +224 -0
- package/autopm/.claude/lib/task-utils.js +64 -0
- package/autopm/.claude/scripts/pm-epic-decompose-local.js +158 -0
- package/autopm/.claude/scripts/pm-epic-list-local.js +103 -0
- package/autopm/.claude/scripts/pm-epic-show-local.js +70 -0
- package/autopm/.claude/scripts/pm-epic-update-local.js +56 -0
- package/autopm/.claude/scripts/pm-prd-list-local.js +111 -0
- package/autopm/.claude/scripts/pm-prd-new-local.js +196 -0
- package/autopm/.claude/scripts/pm-prd-parse-local.js +360 -0
- package/autopm/.claude/scripts/pm-prd-show-local.js +101 -0
- package/autopm/.claude/scripts/pm-prd-update-local.js +153 -0
- package/autopm/.claude/scripts/pm-sync-download-local.js +424 -0
- package/autopm/.claude/scripts/pm-sync-upload-local.js +473 -0
- package/autopm/.claude/scripts/pm-task-list-local.js +86 -0
- package/autopm/.claude/scripts/pm-task-show-local.js +92 -0
- package/autopm/.claude/scripts/pm-task-update-local.js +109 -0
- package/autopm/.claude/scripts/setup-local-mode.js +127 -0
- package/package.json +5 -3
- package/scripts/create-task-issues.sh +26 -0
- package/scripts/fix-invalid-command-refs.sh +4 -3
- 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.
|
|
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
|
-
"@
|
|
153
|
-
"@
|
|
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 '' "
|
|
77
|
+
sed -i '' -E "/([[:space:]]|^)${cmd}([[:space:]]|\$|[[:punct:]])/d" "$file"
|
|
77
78
|
else
|
|
78
|
-
sed -i "
|
|
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
|
-
|
|
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
|
-
((
|
|
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-*)"
|