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.
Files changed (38) hide show
  1. package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -18
  2. package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -18
  3. package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -18
  4. package/autopm/.claude/agents/frameworks/react-ui-expert.md +1 -18
  5. package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +1 -18
  6. package/autopm/.claude/agents/frameworks/ux-design-expert.md +1 -18
  7. package/autopm/.claude/agents/languages/bash-scripting-expert.md +1 -18
  8. package/autopm/.claude/agents/languages/javascript-frontend-engineer.md +1 -18
  9. package/autopm/.claude/agents/languages/nodejs-backend-engineer.md +1 -18
  10. package/autopm/.claude/agents/languages/python-backend-engineer.md +1 -18
  11. package/autopm/.claude/agents/languages/python-backend-expert.md +1 -18
  12. package/autopm/.claude/commands/pm/epic-decompose.md +19 -5
  13. package/autopm/.claude/commands/pm/prd-new.md +14 -1
  14. package/autopm/.claude/includes/task-creation-excellence.md +18 -0
  15. package/autopm/.claude/lib/ai-task-generator.js +84 -0
  16. package/autopm/.claude/lib/cli-parser.js +148 -0
  17. package/autopm/.claude/lib/dependency-analyzer.js +157 -0
  18. package/autopm/.claude/lib/frontmatter.js +224 -0
  19. package/autopm/.claude/lib/task-utils.js +64 -0
  20. package/autopm/.claude/scripts/pm-epic-decompose-local.js +158 -0
  21. package/autopm/.claude/scripts/pm-epic-list-local.js +103 -0
  22. package/autopm/.claude/scripts/pm-epic-show-local.js +70 -0
  23. package/autopm/.claude/scripts/pm-epic-update-local.js +56 -0
  24. package/autopm/.claude/scripts/pm-prd-list-local.js +111 -0
  25. package/autopm/.claude/scripts/pm-prd-new-local.js +196 -0
  26. package/autopm/.claude/scripts/pm-prd-parse-local.js +360 -0
  27. package/autopm/.claude/scripts/pm-prd-show-local.js +101 -0
  28. package/autopm/.claude/scripts/pm-prd-update-local.js +153 -0
  29. package/autopm/.claude/scripts/pm-sync-download-local.js +424 -0
  30. package/autopm/.claude/scripts/pm-sync-upload-local.js +473 -0
  31. package/autopm/.claude/scripts/pm-task-list-local.js +86 -0
  32. package/autopm/.claude/scripts/pm-task-show-local.js +92 -0
  33. package/autopm/.claude/scripts/pm-task-update-local.js +109 -0
  34. package/autopm/.claude/scripts/setup-local-mode.js +127 -0
  35. package/package.json +5 -3
  36. package/scripts/create-task-issues.sh +26 -0
  37. package/scripts/fix-invalid-command-refs.sh +4 -3
  38. package/scripts/fix-invalid-refs-simple.sh +8 -3
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Show Local Epic
3
+ *
4
+ * Displays details of a specific epic including frontmatter,
5
+ * body content, and directory information.
6
+ *
7
+ * Usage:
8
+ * const { showLocalEpic } = require('./pm-epic-show-local');
9
+ *
10
+ * const epic = await showLocalEpic('epic-001');
11
+ * console.log(epic.frontmatter.title);
12
+ * console.log(epic.body);
13
+ */
14
+
15
+ const fs = require('fs').promises;
16
+ const path = require('path');
17
+ const { parseFrontmatter } = require('../lib/frontmatter');
18
+
19
+ /**
20
+ * Get epic details by ID
21
+ *
22
+ * @param {string} epicId - Epic ID to retrieve
23
+ * @returns {Promise<Object>} Epic object with frontmatter, body, and directory
24
+ * @throws {Error} If epic not found
25
+ */
26
+ async function showLocalEpic(epicId) {
27
+ const basePath = process.cwd();
28
+ const epicsDir = path.join(basePath, '.claude', 'epics');
29
+
30
+ // Check if epics directory exists
31
+ try {
32
+ await fs.access(epicsDir);
33
+ } catch (err) {
34
+ if (err.code === 'ENOENT') {
35
+ throw new Error(`Epic not found: ${epicId} (epics directory does not exist)`);
36
+ }
37
+ throw err;
38
+ }
39
+
40
+ // Find epic directory by ID
41
+ const dirs = await fs.readdir(epicsDir);
42
+ const epicDir = dirs.find(dir => dir.startsWith(`${epicId}-`));
43
+
44
+ if (!epicDir) {
45
+ throw new Error(`Epic not found: ${epicId}`);
46
+ }
47
+
48
+ const epicDirPath = path.join(epicsDir, epicDir);
49
+ const epicPath = path.join(epicDirPath, 'epic.md');
50
+
51
+ // Read and parse epic file
52
+ try {
53
+ const content = await fs.readFile(epicPath, 'utf8');
54
+ const { frontmatter, body } = parseFrontmatter(content);
55
+
56
+ return {
57
+ frontmatter,
58
+ body,
59
+ directory: epicDir,
60
+ path: epicPath
61
+ };
62
+ } catch (err) {
63
+ if (err.code === 'ENOENT') {
64
+ throw new Error(`Epic not found: ${epicId} (epic.md missing in ${epicDir})`);
65
+ }
66
+ throw err;
67
+ }
68
+ }
69
+
70
+ module.exports = { showLocalEpic };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Update Local Epic
3
+ *
4
+ * Updates epic frontmatter fields while preserving body content.
5
+ * Supports updating single or multiple fields at once.
6
+ *
7
+ * Usage:
8
+ * const { updateLocalEpic } = require('./pm-epic-update-local');
9
+ *
10
+ * // Update status
11
+ * await updateLocalEpic('epic-001', { status: 'in_progress' });
12
+ *
13
+ * // Update multiple fields
14
+ * await updateLocalEpic('epic-001', {
15
+ * status: 'completed',
16
+ * tasks_completed: 5
17
+ * });
18
+ */
19
+
20
+ const fs = require('fs').promises;
21
+ const path = require('path');
22
+ const { parseFrontmatter, stringifyFrontmatter } = require('../lib/frontmatter');
23
+ const { showLocalEpic } = require('./pm-epic-show-local');
24
+
25
+ /**
26
+ * Update epic frontmatter fields
27
+ *
28
+ * @param {string} epicId - Epic ID to update
29
+ * @param {Object} updates - Fields to update in frontmatter
30
+ * @returns {Promise<Object>} Updated epic object
31
+ * @throws {Error} If epic not found
32
+ */
33
+ async function updateLocalEpic(epicId, updates) {
34
+ // Get current epic
35
+ const epic = await showLocalEpic(epicId);
36
+
37
+ // Merge updates into frontmatter
38
+ const updatedFrontmatter = {
39
+ ...epic.frontmatter,
40
+ ...updates
41
+ };
42
+
43
+ // Preserve body content
44
+ const updatedContent = stringifyFrontmatter(updatedFrontmatter, epic.body);
45
+
46
+ // Write updated content back to file
47
+ await fs.writeFile(epic.path, updatedContent, 'utf8');
48
+
49
+ return {
50
+ epicId,
51
+ frontmatter: updatedFrontmatter,
52
+ body: epic.body
53
+ };
54
+ }
55
+
56
+ module.exports = { updateLocalEpic };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Local PRD Listing Command
3
+ *
4
+ * Lists all Product Requirements Documents (PRDs) in local mode.
5
+ * Supports filtering and sorting.
6
+ *
7
+ * Usage:
8
+ * /pm:prd-list --local
9
+ * /pm:prd-list --local --status approved
10
+ *
11
+ * @module pm-prd-list-local
12
+ */
13
+
14
+ const fs = require('fs').promises;
15
+ const path = require('path');
16
+ const { parseFrontmatter } = require('../lib/frontmatter');
17
+
18
+ /**
19
+ * Lists all local PRDs with optional filtering
20
+ *
21
+ * @param {Object} options - Listing options
22
+ * @param {string} options.status - Filter by status (draft, approved, etc.)
23
+ * @returns {Promise<Array>} Array of PRD metadata objects
24
+ */
25
+ async function listLocalPRDs(options = {}) {
26
+ const prdsDir = path.join(process.cwd(), '.claude', 'prds');
27
+
28
+ // Ensure directory exists
29
+ try {
30
+ await fs.access(prdsDir);
31
+ } catch (err) {
32
+ if (err.code === 'ENOENT') {
33
+ return []; // No PRDs directory = no PRDs
34
+ }
35
+ throw err;
36
+ }
37
+
38
+ // Read directory
39
+ const files = await fs.readdir(prdsDir);
40
+ const mdFiles = files.filter(f => f.endsWith('.md'));
41
+
42
+ // Parallelize file reading/parsing with Promise.allSettled
43
+ const prdPromises = mdFiles.map(async (file) => {
44
+ try {
45
+ const filepath = path.join(prdsDir, file);
46
+ const content = await fs.readFile(filepath, 'utf8');
47
+ const { frontmatter } = parseFrontmatter(content);
48
+ // Only include files with valid frontmatter containing required fields
49
+ // A valid PRD must have at least an 'id' field
50
+ if (frontmatter && typeof frontmatter === 'object' && frontmatter.id) {
51
+ return {
52
+ filename: file,
53
+ ...frontmatter
54
+ };
55
+ }
56
+ } catch (err) {
57
+ // Skip files that can't be parsed
58
+ console.warn(`Warning: Could not parse ${file}:`, err.message);
59
+ }
60
+ return null;
61
+ });
62
+
63
+ const settled = await Promise.allSettled(prdPromises);
64
+ const prds = settled
65
+ .filter(r => r.status === 'fulfilled' && r.value)
66
+ .map(r => r.value);
67
+ // Filter by status if specified
68
+ let filtered = prds;
69
+ if (options.status) {
70
+ filtered = filtered.filter(p => p.status === options.status);
71
+ }
72
+
73
+ // Sort by creation timestamp (newest first)
74
+ filtered.sort((a, b) => {
75
+ // Use createdAt if available (full timestamp), fallback to created (date only)
76
+ const dateA = new Date(a.createdAt || a.created || 0);
77
+ const dateB = new Date(b.createdAt || b.created || 0);
78
+ return dateB - dateA;
79
+ });
80
+
81
+ return filtered;
82
+ }
83
+
84
+ /**
85
+ * Formats PRD list for display
86
+ *
87
+ * @param {Array} prds - Array of PRD objects
88
+ * @returns {string} Formatted string for display
89
+ */
90
+ function formatPRDList(prds) {
91
+ if (prds.length === 0) {
92
+ return 'No PRDs found.';
93
+ }
94
+
95
+ const lines = ['', 'Local PRDs:', ''];
96
+
97
+ prds.forEach((prd, index) => {
98
+ lines.push(
99
+ `${index + 1}. [${prd.id}] ${prd.title}`,
100
+ ` Status: ${prd.status} | Priority: ${prd.priority} | Created: ${prd.created}`,
101
+ ''
102
+ );
103
+ });
104
+
105
+ return lines.join('\n');
106
+ }
107
+
108
+ module.exports = {
109
+ listLocalPRDs,
110
+ formatPRDList
111
+ };
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Local PRD Creation Command
3
+ *
4
+ * Creates a new Product Requirements Document (PRD) in local mode.
5
+ * PRDs are stored in .claude/prds/ directory.
6
+ *
7
+ * Usage:
8
+ * /pm:prd-new --local "Feature Name"
9
+ *
10
+ * @module pm-prd-new-local
11
+ */
12
+
13
+ const fs = require('fs').promises;
14
+ const path = require('path');
15
+ const { stringifyFrontmatter } = require('../lib/frontmatter');
16
+
17
+ /**
18
+ * Creates a new PRD in the local .claude/prds/ directory
19
+ *
20
+ * @param {string} name - PRD title/name
21
+ * @param {Object} options - Optional configuration
22
+ * @param {string} options.id - Custom PRD ID (auto-generated if not provided)
23
+ * @param {string} options.author - Author name (default: 'ClaudeAutoPM')
24
+ * @param {string} options.priority - Priority level (default: 'medium')
25
+ * @returns {Promise<Object>} Created PRD metadata
26
+ * @throws {Error} If name is invalid or PRD already exists
27
+ */
28
+ async function createLocalPRD(name, options = {}) {
29
+ // Validate name
30
+ if (!name || typeof name !== 'string' || name.trim().length === 0) {
31
+ throw new Error('PRD name is required and must be a non-empty string');
32
+ }
33
+
34
+ const sanitizedName = name.trim();
35
+
36
+ // Generate unique ID
37
+ const id = options.id || generatePRDId();
38
+
39
+ // Create frontmatter
40
+ const now = new Date();
41
+ const frontmatter = {
42
+ id,
43
+ title: sanitizedName,
44
+ created: now.toISOString().split('T')[0],
45
+ createdAt: now.toISOString(), // Include full timestamp for sorting
46
+ author: options.author || 'ClaudeAutoPM',
47
+ status: 'draft',
48
+ priority: options.priority || 'medium',
49
+ version: '1.0'
50
+ };
51
+
52
+ // Create PRD body template
53
+ const body = createPRDTemplate(sanitizedName);
54
+
55
+ // Generate full markdown content
56
+ const content = stringifyFrontmatter(frontmatter, body);
57
+
58
+ // Generate filename (sanitize name + add ID for uniqueness)
59
+ const filename = `${id}-${sanitizeFilename(sanitizedName)}`;
60
+ const filepath = path.join(process.cwd(), '.claude', 'prds', filename);
61
+
62
+ // Check if file already exists
63
+ try {
64
+ await fs.access(filepath);
65
+ throw new Error(`PRD already exists: ${filename}`);
66
+ } catch (err) {
67
+ if (err.code !== 'ENOENT') {
68
+ throw err;
69
+ }
70
+ }
71
+
72
+ // Ensure directory exists
73
+ await fs.mkdir(path.dirname(filepath), { recursive: true });
74
+
75
+ // Write to file
76
+ await fs.writeFile(filepath, content, 'utf8');
77
+
78
+ return {
79
+ id,
80
+ filepath,
81
+ frontmatter
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Creates a PRD template with standard sections
87
+ *
88
+ * @param {string} name - PRD title
89
+ * @returns {string} PRD template content
90
+ */
91
+ function createPRDTemplate(name) {
92
+ return `# Product Requirements Document: ${name}
93
+
94
+ ## 1. Executive Summary
95
+
96
+ ### Overview
97
+ [Describe the feature/product in 2-3 sentences]
98
+
99
+ ### Business Value
100
+ [Why is this important?]
101
+
102
+ ### Success Metrics
103
+ [How will we measure success?]
104
+
105
+ ## 2. Background
106
+
107
+ ### Problem Statement
108
+ [What problem are we solving?]
109
+
110
+ ### Current State
111
+ [What exists today?]
112
+
113
+ ### Goals and Objectives
114
+ [What are we trying to achieve?]
115
+
116
+ ## 3. User Stories
117
+
118
+ [Epic-level user stories]
119
+
120
+ ## 4. Functional Requirements
121
+
122
+ [Detailed requirements]
123
+
124
+ ## 5. Non-Functional Requirements
125
+
126
+ [Performance, security, etc.]
127
+
128
+ ## 6. Out of Scope
129
+
130
+ [What we're NOT doing]
131
+
132
+ ## 7. Timeline
133
+
134
+ [Key milestones]
135
+ `;
136
+ }
137
+
138
+ // Counter for ensuring unique IDs even when created at the same millisecond
139
+ let idCounter = 0;
140
+
141
+ /**
142
+ * Generates a unique PRD ID
143
+ *
144
+ * Format: prd-XXX (3 digits from timestamp + counter)
145
+ *
146
+ * @returns {string} Generated PRD ID
147
+ */
148
+ function generatePRDId() {
149
+ const timestamp = Date.now();
150
+ const random = Math.floor(Math.random() * 900) + 100; // 100-999
151
+ const counter = (idCounter++) % 10; // 0-9 cycling counter
152
+
153
+ // Use last digit of timestamp + last 2 digits of random = 3 digits total
154
+ const suffix = (timestamp % 10).toString() +
155
+ (Math.floor(random / 10) % 10).toString() +
156
+ counter.toString();
157
+
158
+ return `prd-${suffix}`;
159
+ }
160
+
161
+ /**
162
+ * Sanitizes a PRD name for use as a filename
163
+ *
164
+ * - Converts to lowercase
165
+ * - Replaces spaces with hyphens
166
+ * - Removes special characters except hyphens
167
+ * - Truncates to safe length (max 100 chars before .md)
168
+ * - Adds .md extension
169
+ *
170
+ * @param {string} name - PRD name to sanitize
171
+ * @returns {string} Sanitized filename
172
+ */
173
+ function sanitizeFilename(name) {
174
+ const sanitized = name
175
+ .toLowerCase()
176
+ .replace(/\s+/g, '-') // Replace spaces with hyphens
177
+ .replace(/[^a-z0-9-]/g, '') // Remove special characters
178
+ .replace(/-+/g, '-') // Collapse multiple hyphens
179
+ .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
180
+
181
+ // Truncate to safe length (100 chars + .md = 103 total)
182
+ // With prd-XXX- prefix (8 chars), max base name is ~92 chars
183
+ const maxLength = 92;
184
+ const truncated = sanitized.length > maxLength
185
+ ? sanitized.substring(0, maxLength)
186
+ : sanitized;
187
+
188
+ return truncated + '.md';
189
+ }
190
+
191
+ module.exports = {
192
+ createLocalPRD,
193
+ createPRDTemplate,
194
+ generatePRDId,
195
+ sanitizeFilename
196
+ };