claude-autopm 3.21.0 → 3.22.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.
@@ -0,0 +1,12 @@
1
+ ---
2
+ allowed-tools: Bash, Read
3
+ ---
4
+
5
+ # Diagram List
6
+
7
+ List available project diagrams.
8
+
9
+ ## Usage
10
+ ```bash
11
+ node .claude/scripts/pm/diagram-list.js
12
+ ```
@@ -0,0 +1,85 @@
1
+ ---
2
+ allowed-tools: Bash, Read, Write, Glob, Grep
3
+ ---
4
+
5
+ # Diagram New
6
+
7
+ Create a project architecture diagram by analyzing the codebase.
8
+
9
+ ## Usage
10
+ /pm:diagram-new <name> [--type architecture|modules|data-flow|dependencies]
11
+
12
+ ## Instructions
13
+
14
+ 1. Determine diagram type from --type flag or infer from project:
15
+ - **architecture**: High-level system components and how they connect
16
+ - **modules**: Import/require dependency graph between source files
17
+ - **data-flow**: How data moves through the system (API → service → DB)
18
+ - **dependencies**: package.json dependency tree
19
+
20
+ 2. Analyze the project:
21
+ - Read package.json for dependencies and project type
22
+ - Scan src/ or main source directory for imports/requires
23
+ - Check for docker-compose.yml, Dockerfile
24
+ - Check for API routes (Express, FastAPI, etc.)
25
+ - Check for database models/schemas
26
+ - Check for .env for external service connections
27
+ **NEVER include actual .env values (tokens, passwords, secrets) in diagram or metadata.**
28
+ Only infer service names/types from variable names, not values.
29
+
30
+ 3. Generate Mermaid diagram based on analysis:
31
+ - Use `graph TD` for hierarchical, `graph LR` for flow
32
+ - Group related modules in `subgraph` blocks
33
+ - Use icons: databases [(DB)], services [Service], external{{External}}
34
+ - Color-code using Mermaid classDef:
35
+ ```
36
+ classDef active fill:#d4f8db,stroke:#2f855a
37
+ classDef config fill:#e2e8f0,stroke:#4a5568
38
+ classDef api fill:#ebf4ff,stroke:#2b6cb0
39
+ class FastAPI api
40
+ class PostgreSQL active
41
+ ```
42
+
43
+ 4. Save diagram:
44
+ ```bash
45
+ mkdir -p .claude/pm/diagrams
46
+ ```
47
+ Write Mermaid syntax to `.claude/pm/diagrams/<name>.mmd`
48
+ Write metadata to `.claude/pm/diagrams/<name>.meta.json`:
49
+ ```json
50
+ {"name":"<name>","type":"<type>","created":"<ISO>","updated":"<ISO>","scope":"src/"}
51
+ ```
52
+
53
+ 5. Display the diagram in terminal (show the Mermaid source)
54
+
55
+ ## Example Output
56
+
57
+ For a FastAPI + React project:
58
+ ```mermaid
59
+ graph TD
60
+ subgraph Frontend
61
+ React[React App]
62
+ Redux[Redux Store]
63
+ API_Client[API Client]
64
+ end
65
+
66
+ subgraph Backend
67
+ FastAPI[FastAPI Server]
68
+ Auth[Auth Module]
69
+ Users[Users API]
70
+ ORM[SQLAlchemy ORM]
71
+ end
72
+
73
+ subgraph Data
74
+ PostgreSQL[(PostgreSQL)]
75
+ Redis[(Redis Cache)]
76
+ end
77
+
78
+ React --> API_Client
79
+ API_Client --> FastAPI
80
+ FastAPI --> Auth
81
+ FastAPI --> Users
82
+ Users --> ORM
83
+ ORM --> PostgreSQL
84
+ Auth --> Redis
85
+ ```
@@ -0,0 +1,14 @@
1
+ ---
2
+ allowed-tools: Bash, Read
3
+ ---
4
+
5
+ # Diagram Show
6
+
7
+ Display a project diagram.
8
+
9
+ ## Usage
10
+ ```bash
11
+ node .claude/scripts/pm/diagram-list.js --show <name>
12
+ ```
13
+
14
+ Shows the Mermaid source of the diagram. View rendered version in `/pm:dashboard` Diagrams tab.
@@ -0,0 +1,24 @@
1
+ ---
2
+ allowed-tools: Bash, Read, Write, Glob, Grep
3
+ ---
4
+
5
+ # Diagram Update
6
+
7
+ Update an existing project diagram after code changes.
8
+
9
+ ## Usage
10
+ /pm:diagram-update <name>
11
+
12
+ ## Instructions
13
+
14
+ 1. Read existing diagram from `.claude/pm/diagrams/<name>.mmd`
15
+ 2. Read metadata from `.claude/pm/diagrams/<name>.meta.json` for type and scope
16
+ 3. If either file is missing: `❌ Diagram not found: Run /pm:diagram-new <name>`
17
+ 4. Re-analyze the project (same analysis as diagram-new, scoped to metadata scope)
18
+ 5. Compare with existing diagram — identify:
19
+ - New modules/components added
20
+ - Removed modules
21
+ - Changed connections
22
+ 6. Update the .mmd file with changes
23
+ 7. Update metadata: set `updated` to current timestamp (`date -u +"%Y-%m-%dT%H:%M:%SZ"`)
24
+ 8. Show diff summary: "Added: X, Removed: Y, Updated: Z connections"
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: autopm
3
+ command: node
4
+ args: [".claude/scripts/mcp/autopm-server.js"]
5
+ description: AutoPM project management — issues, epics, PRDs, learnings, checkpoints
6
+ category: project-management
7
+ status: inactive
8
+ ---
9
+
10
+ # AutoPM MCP Server
11
+
12
+ Local MCP server exposing AutoPM project management data. Reuses local providers — same data as slash commands.
13
+
14
+ ## Tools (13)
15
+ - `autopm_list_issues` — list issues (filter by status)
16
+ - `autopm_show_issue` — issue details
17
+ - `autopm_create_issue` — create new issue
18
+ - `autopm_start_issue` — start working (set in_progress)
19
+ - `autopm_close_issue` — close issue
20
+ - `autopm_list_epics` — list epics with progress
21
+ - `autopm_show_epic` — epic with tasks
22
+ - `autopm_list_prds` — list PRDs
23
+ - `autopm_show_prd` — PRD details
24
+ - `autopm_status` — project overview
25
+ - `autopm_learn` — save learning
26
+ - `autopm_recall` — get learnings
27
+ - `autopm_checkpoint` — create checkpoint
28
+
29
+ ## Resources (5)
30
+ - `autopm://config` — config.json
31
+ - `autopm://agents` — agent-registry.xml
32
+ - `autopm://events` — recent events
33
+ - `autopm://learnings` — all learnings
34
+ - `autopm://test-plan` — test plan
35
+
36
+ ## Prompts (3)
37
+ - `autopm_issue_template` — issue XML template
38
+ - `autopm_prd_template` — PRD XML template
39
+ - `autopm_epic_template` — epic XML template
40
+
41
+ ## Enable
42
+ ```bash
43
+ autopm mcp enable autopm
44
+ ```
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AutoPM MCP Server
4
+ *
5
+ * Exposes PM data (issues, epics, PRDs, learnings, config) via Model Context Protocol.
6
+ * Reuses local providers as backend — no data duplication.
7
+ *
8
+ * Usage:
9
+ * node .claude/scripts/mcp/autopm-server.js
10
+ *
11
+ * In .claude/mcp-servers.json:
12
+ * { "autopm": { "command": "node", "args": [".claude/scripts/mcp/autopm-server.js"] } }
13
+ */
14
+
15
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
16
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
17
+ const {
18
+ CallToolRequestSchema,
19
+ ListToolsRequestSchema,
20
+ ListResourcesRequestSchema,
21
+ ReadResourceRequestSchema,
22
+ ListPromptsRequestSchema,
23
+ GetPromptRequestSchema
24
+ } = require('@modelcontextprotocol/sdk/types.js');
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+
29
+ const basePath = process.cwd();
30
+ const settings = { basePath };
31
+
32
+ // Lazy-load providers to avoid errors when files don't exist
33
+ function loadProvider(name) {
34
+ const providerPath = path.join(basePath, '.claude', 'providers', 'local', name + '.js');
35
+ if (fs.existsSync(providerPath)) return require(providerPath);
36
+ // Fallback to autopm path
37
+ const autopmPath = path.join(__dirname, '..', '..', 'providers', 'local', name + '.js');
38
+ if (fs.existsSync(autopmPath)) return require(autopmPath);
39
+ return null;
40
+ }
41
+
42
+ function readFile(relativePath) {
43
+ const fullPath = path.join(basePath, relativePath);
44
+ if (fs.existsSync(fullPath)) return fs.readFileSync(fullPath, 'utf8');
45
+ return null;
46
+ }
47
+
48
+ function readJSON(relativePath) {
49
+ const content = readFile(relativePath);
50
+ if (!content) return null;
51
+ try { return JSON.parse(content); } catch { return null; }
52
+ }
53
+
54
+ // ── Server Setup ──
55
+
56
+ const server = new Server(
57
+ { name: 'autopm', version: '1.0.0' },
58
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
59
+ );
60
+
61
+ // ── Tools ──
62
+
63
+ const TOOLS = [
64
+ { name: 'autopm_list_issues', description: 'List local issues with optional status filter', inputSchema: { type: 'object', properties: { status: { type: 'string', description: 'Filter: open, in_progress, closed' } } } },
65
+ { name: 'autopm_show_issue', description: 'Show issue details by ID', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'Issue number' } }, required: ['id'] } },
66
+ { name: 'autopm_create_issue', description: 'Create a new local issue', inputSchema: { type: 'object', properties: { title: { type: 'string' }, labels: { type: 'array', items: { type: 'string' } }, body: { type: 'string' } }, required: ['title'] } },
67
+ { name: 'autopm_start_issue', description: 'Start working on an issue (set in_progress)', inputSchema: { type: 'object', properties: { id: { type: 'number' }, no_branch: { type: 'boolean' } }, required: ['id'] } },
68
+ { name: 'autopm_close_issue', description: 'Close an issue', inputSchema: { type: 'object', properties: { id: { type: 'number' } }, required: ['id'] } },
69
+ { name: 'autopm_list_epics', description: 'List epics with progress', inputSchema: { type: 'object', properties: { status: { type: 'string' } } } },
70
+ { name: 'autopm_show_epic', description: 'Show epic with tasks', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } },
71
+ { name: 'autopm_list_prds', description: 'List PRDs', inputSchema: { type: 'object', properties: { status: { type: 'string' } } } },
72
+ { name: 'autopm_show_prd', description: 'Show PRD details', inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } },
73
+ { name: 'autopm_status', description: 'Project overview — counts, recent activity', inputSchema: { type: 'object', properties: {} } },
74
+ { name: 'autopm_learn', description: 'Save a project learning', inputSchema: { type: 'object', properties: { learning: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } } }, required: ['learning'] } },
75
+ { name: 'autopm_recall', description: 'Get project learnings', inputSchema: { type: 'object', properties: { tag: { type: 'string' }, limit: { type: 'number' } } } },
76
+ { name: 'autopm_checkpoint', description: 'Create a project checkpoint', inputSchema: { type: 'object', properties: { description: { type: 'string' } }, required: ['description'] } }
77
+ ];
78
+
79
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
80
+
81
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
82
+ const { name, arguments: args } = request.params;
83
+
84
+ try {
85
+ switch (name) {
86
+ case 'autopm_list_issues': {
87
+ const provider = loadProvider('issue-list');
88
+ if (!provider) return text('Issue list provider not available');
89
+ const result = await provider.execute(args || {}, settings);
90
+ return text(JSON.stringify(result, null, 2));
91
+ }
92
+ case 'autopm_show_issue': {
93
+ const provider = loadProvider('issue-show');
94
+ if (!provider) return text('Issue show provider not available');
95
+ const result = await provider.execute(args, settings);
96
+ return text(JSON.stringify(result, null, 2));
97
+ }
98
+ case 'autopm_create_issue': {
99
+ const provider = loadProvider('issue-create');
100
+ if (!provider) return text('Issue create provider not available');
101
+ const result = await provider.execute(args, settings);
102
+ return text(JSON.stringify(result, null, 2));
103
+ }
104
+ case 'autopm_start_issue': {
105
+ const provider = loadProvider('issue-start');
106
+ if (!provider) return text('Issue start provider not available');
107
+ const result = await provider.execute(args, settings);
108
+ return text(JSON.stringify(result, null, 2));
109
+ }
110
+ case 'autopm_close_issue': {
111
+ const provider = loadProvider('issue-close');
112
+ if (!provider) return text('Issue close provider not available');
113
+ const result = await provider.execute(args, settings);
114
+ return text(JSON.stringify(result, null, 2));
115
+ }
116
+ case 'autopm_list_epics': {
117
+ const provider = loadProvider('epic-list');
118
+ if (!provider) return text('Epic list provider not available');
119
+ const result = await provider.execute(args || {}, settings);
120
+ return text(JSON.stringify(result, null, 2));
121
+ }
122
+ case 'autopm_show_epic': {
123
+ const provider = loadProvider('epic-show');
124
+ if (!provider) return text('Epic show provider not available');
125
+ const result = await provider.execute(args, settings);
126
+ return text(JSON.stringify(result, null, 2));
127
+ }
128
+ case 'autopm_list_prds': {
129
+ const provider = loadProvider('prd-list');
130
+ if (!provider) return text('PRD list provider not available');
131
+ const result = await provider.execute(args || {}, settings);
132
+ return text(JSON.stringify(result, null, 2));
133
+ }
134
+ case 'autopm_show_prd': {
135
+ const provider = loadProvider('prd-show');
136
+ if (!provider) return text('PRD show provider not available');
137
+ const result = await provider.execute(args, settings);
138
+ return text(JSON.stringify(result, null, 2));
139
+ }
140
+ case 'autopm_status': {
141
+ const issues = loadProvider('issue-list');
142
+ const epics = loadProvider('epic-list');
143
+ const prds = loadProvider('prd-list');
144
+ const status = {
145
+ issues: issues ? await issues.execute({}, settings) : { count: 0 },
146
+ epics: epics ? await epics.execute({}, settings) : { count: 0 },
147
+ prds: prds ? await prds.execute({}, settings) : { count: 0 }
148
+ };
149
+ // Add recent events
150
+ try {
151
+ const loggerPath = path.join(basePath, '.claude', 'lib', 'event-logger');
152
+ const { readEvents } = require(loggerPath);
153
+ status.recentEvents = readEvents(10, null, basePath);
154
+ } catch { status.recentEvents = []; }
155
+ return text(JSON.stringify(status, null, 2));
156
+ }
157
+ case 'autopm_learn': {
158
+ try {
159
+ const learningsPath = path.join(basePath, '.claude', 'pm', 'learnings.jsonl');
160
+ const dir = path.dirname(learningsPath);
161
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
162
+ const entry = { timestamp: new Date().toISOString(), type: 'learning', learning: args.learning, tags: args.tags || [] };
163
+ fs.appendFileSync(learningsPath, JSON.stringify(entry) + '\n');
164
+ try {
165
+ const loggerPath = path.join(basePath, '.claude', 'lib', 'event-logger');
166
+ const { logEvent } = require(loggerPath);
167
+ logEvent('learning.saved', { learning: args.learning, tags: args.tags || [] }, basePath);
168
+ } catch { /* best effort */ }
169
+ return text(JSON.stringify({ success: true, learning: args.learning }));
170
+ } catch (e) { return text(JSON.stringify({ error: e.message })); }
171
+ }
172
+ case 'autopm_recall': {
173
+ const learningsPath = path.join(basePath, '.claude', 'pm', 'learnings.jsonl');
174
+ if (!fs.existsSync(learningsPath)) return text(JSON.stringify({ learnings: [] }));
175
+ let entries = fs.readFileSync(learningsPath, 'utf8').trim().split('\n')
176
+ .map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
177
+ if (args && args.tag) entries = entries.filter(e => (e.tags || []).includes(args.tag));
178
+ const limit = (args && args.limit) || 20;
179
+ return text(JSON.stringify({ learnings: entries.slice(-limit).reverse() }));
180
+ }
181
+ case 'autopm_checkpoint': {
182
+ try {
183
+ const { execSync } = require('child_process');
184
+ const cpDir = path.join(basePath, '.claude', 'pm', 'checkpoints');
185
+ if (!fs.existsSync(cpDir)) fs.mkdirSync(cpDir, { recursive: true });
186
+ const now = new Date().toISOString();
187
+ let gitInfo = {};
188
+ try {
189
+ gitInfo.branch = execSync('git branch --show-current', { encoding: 'utf8', cwd: basePath }).trim();
190
+ gitInfo.hash = execSync('git rev-parse --short HEAD', { encoding: 'utf8', cwd: basePath }).trim();
191
+ gitInfo.clean = !execSync('git status --porcelain', { encoding: 'utf8', cwd: basePath }).trim();
192
+ } catch { /* not a git repo */ }
193
+ // Count PM artifacts to match CLI checkpoint format
194
+ const counts = {};
195
+ for (const [key, dir] of [['issues', 'issues'], ['epics', 'epics'], ['prds', 'prds']]) {
196
+ const d = path.join(basePath, '.claude', dir);
197
+ try { counts[key] = fs.readdirSync(d).filter(f => f.endsWith('.md')).length; } catch { counts[key] = 0; }
198
+ }
199
+ // Load recent learnings
200
+ let learnings = [];
201
+ try {
202
+ const lPath = path.join(basePath, '.claude', 'pm', 'learnings.jsonl');
203
+ if (fs.existsSync(lPath)) {
204
+ learnings = fs.readFileSync(lPath, 'utf8').trim().split('\n')
205
+ .map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean).slice(-5);
206
+ }
207
+ } catch { /* ignore */ }
208
+ const checkpoint = { timestamp: now, description: args.description, git: gitInfo, counts, learnings, config_snapshot: null };
209
+ fs.writeFileSync(path.join(cpDir, now.replace(/[:.]/g, '-') + '.json'), JSON.stringify(checkpoint, null, 2));
210
+ return text(JSON.stringify({ success: true, checkpoint }));
211
+ } catch (e) { return text(JSON.stringify({ error: e.message })); }
212
+ }
213
+ default:
214
+ return text(`Unknown tool: ${name}`);
215
+ }
216
+ } catch (e) {
217
+ return text(JSON.stringify({ error: e.message }));
218
+ }
219
+ });
220
+
221
+ // ── Resources ──
222
+
223
+ const RESOURCES = [
224
+ { uri: 'autopm://config', name: 'Project Config', description: 'Current .claude/config.json', mimeType: 'application/json' },
225
+ { uri: 'autopm://agents', name: 'Agent Registry', description: 'Loaded agents from agent-registry.xml', mimeType: 'text/xml' },
226
+ { uri: 'autopm://events', name: 'Recent Events', description: 'Last 50 events from events.jsonl', mimeType: 'application/json' },
227
+ { uri: 'autopm://learnings', name: 'Project Learnings', description: 'All learnings from learnings.jsonl', mimeType: 'application/json' },
228
+ { uri: 'autopm://test-plan', name: 'Test Plan', description: 'Current test plan', mimeType: 'text/markdown' }
229
+ ];
230
+
231
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: RESOURCES }));
232
+
233
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
234
+ const { uri } = request.params;
235
+
236
+ switch (uri) {
237
+ case 'autopm://config':
238
+ return resource(uri, readFile('.claude/config.json') || '{}', 'application/json');
239
+ case 'autopm://agents':
240
+ return resource(uri, readFile('.claude/agents/agent-registry.xml') || '<agent-registry/>', 'text/xml');
241
+ case 'autopm://events': {
242
+ try {
243
+ const loggerPath = path.join(basePath, '.claude', 'lib', 'event-logger');
244
+ const { readEvents } = require(loggerPath);
245
+ return resource(uri, JSON.stringify(readEvents(50, null, basePath), null, 2), 'application/json');
246
+ } catch { return resource(uri, '[]', 'application/json'); }
247
+ }
248
+ case 'autopm://learnings': {
249
+ const content = readFile('.claude/pm/learnings.jsonl');
250
+ if (!content) return resource(uri, '[]', 'application/json');
251
+ const entries = content.trim().split('\n').map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
252
+ return resource(uri, JSON.stringify(entries, null, 2), 'application/json');
253
+ }
254
+ case 'autopm://test-plan':
255
+ return resource(uri, readFile('.claude/pm/test-plan.md') || 'No test plan. Run /pm:test-plan to generate.', 'text/markdown');
256
+ default:
257
+ throw new Error(`Unknown resource: ${uri}`);
258
+ }
259
+ });
260
+
261
+ // ── Prompts ──
262
+
263
+ const PROMPTS = [
264
+ { name: 'autopm_issue_template', description: 'Template for creating a new issue' },
265
+ { name: 'autopm_prd_template', description: 'Template for creating a new PRD' },
266
+ { name: 'autopm_epic_template', description: 'Template for creating a new epic' }
267
+ ];
268
+
269
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: PROMPTS }));
270
+
271
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
272
+ const { name } = request.params;
273
+
274
+ try {
275
+ const templateReaderPath = path.join(basePath, '.claude', 'lib', 'template-reader');
276
+ const { readTemplate, generateMarkdown, resolveTemplatePath } = require(templateReaderPath);
277
+
278
+ const templateMap = {
279
+ autopm_issue_template: 'issue.xml',
280
+ autopm_prd_template: 'prd.xml',
281
+ autopm_epic_template: 'epic.xml'
282
+ };
283
+
284
+ const templateFile = templateMap[name];
285
+ if (!templateFile) throw new Error(`Unknown prompt: ${name}`);
286
+
287
+ const templatePath = resolveTemplatePath(templateFile, basePath);
288
+ const template = readTemplate(templatePath);
289
+ const markdown = generateMarkdown(template, {});
290
+
291
+ return {
292
+ messages: [{
293
+ role: 'user',
294
+ content: [{ type: 'text', text: `Use this template to create a new ${templateFile.replace('.xml', '')}:\n\n${markdown}` }]
295
+ }]
296
+ };
297
+ } catch (e) {
298
+ return {
299
+ messages: [{
300
+ role: 'user',
301
+ content: [{ type: 'text', text: `Template not available: ${e.message}. Run autopm install first.` }]
302
+ }]
303
+ };
304
+ }
305
+ });
306
+
307
+ // ── Helpers ──
308
+
309
+ function text(content) {
310
+ return { content: [{ type: 'text', text: content }] };
311
+ }
312
+
313
+ function resource(uri, content, mimeType) {
314
+ return { contents: [{ uri, text: content, mimeType }] };
315
+ }
316
+
317
+ // ── Start ──
318
+
319
+ async function main() {
320
+ const transport = new StdioServerTransport();
321
+ await server.connect(transport);
322
+ console.error('AutoPM MCP Server running on stdio');
323
+ }
324
+
325
+ main().catch(console.error);
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PM Diagram List/Show Script
5
+ *
6
+ * Lists diagrams from .claude/pm/diagrams/*.mmd with metadata.
7
+ * With --show <name>: displays diagram content.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const DIAGRAMS_DIR = path.join('.claude', 'pm', 'diagrams');
14
+
15
+ function findDiagrams() {
16
+ if (!fs.existsSync(DIAGRAMS_DIR)) {
17
+ return [];
18
+ }
19
+ return fs.readdirSync(DIAGRAMS_DIR)
20
+ .filter(f => f.endsWith('.mmd'))
21
+ .map(f => f.replace('.mmd', ''));
22
+ }
23
+
24
+ function loadMeta(name) {
25
+ const metaPath = path.join(DIAGRAMS_DIR, `${name}.meta.json`);
26
+ if (!fs.existsSync(metaPath)) {
27
+ return { name, type: 'unknown', created: '-', updated: '-' };
28
+ }
29
+ try {
30
+ const raw = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
31
+ return {
32
+ name: raw.name || name,
33
+ type: raw.type || 'unknown',
34
+ created: (raw.created || '-').slice(0, 10),
35
+ updated: (raw.updated || '-').slice(0, 10)
36
+ };
37
+ } catch {
38
+ return { name, type: 'unknown', created: '-', updated: '-' };
39
+ }
40
+ }
41
+
42
+ function listDiagrams() {
43
+ const names = findDiagrams();
44
+ if (names.length === 0) {
45
+ console.log('No diagrams found.');
46
+ console.log('Create one with: /pm:diagram-new <name>');
47
+ return;
48
+ }
49
+
50
+ const rows = names.map(loadMeta);
51
+
52
+ console.log('## Project Diagrams\n');
53
+ console.log('| Name | Type | Created | Updated |');
54
+ console.log('|------|------|---------|---------|');
55
+ for (const r of rows) {
56
+ const escapedName = r.name.replace(/\|/g, '\\|');
57
+ const escapedType = (r.type || '').replace(/\|/g, '\\|');
58
+ console.log(`| ${escapedName} | ${escapedType} | ${r.created} | ${r.updated} |`);
59
+ }
60
+ console.log(`\nTotal: ${rows.length} diagram${rows.length === 1 ? '' : 's'}`);
61
+ console.log('View in dashboard: /pm:dashboard → Diagrams tab');
62
+ }
63
+
64
+ function showDiagram(name) {
65
+ const mmdPath = path.join(DIAGRAMS_DIR, `${name}.mmd`);
66
+ if (!fs.existsSync(mmdPath)) {
67
+ console.error(`❌ Diagram "${name}" not found. Run: /pm:diagram-new ${name}`);
68
+ process.exit(1);
69
+ }
70
+
71
+ const content = fs.readFileSync(mmdPath, 'utf8');
72
+ const meta = loadMeta(name);
73
+
74
+ console.log(`## Diagram: ${meta.name} (${meta.type})\n`);
75
+ console.log('```mermaid');
76
+ console.log(content.trim());
77
+ console.log('```');
78
+ }
79
+
80
+ // Parse args
81
+ const args = process.argv.slice(2);
82
+ const showIdx = args.indexOf('--show');
83
+
84
+ if (showIdx !== -1 && args[showIdx + 1]) {
85
+ const name = args[showIdx + 1];
86
+ const safeName = path.basename(name || '');
87
+ if (!safeName || !/^[a-z0-9_-]+$/i.test(safeName)) {
88
+ console.log('Invalid diagram name. Use alphanumeric, hyphens, underscores only.');
89
+ process.exit(1);
90
+ }
91
+ showDiagram(safeName);
92
+ } else {
93
+ listDiagrams();
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "3.21.0",
3
+ "version": "3.22.0",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "workspaces": [
@@ -126,6 +126,7 @@
126
126
  ],
127
127
  "dependencies": {
128
128
  "@anthropic-ai/sdk": "^0.67.0",
129
+ "@modelcontextprotocol/sdk": "^1.29.0",
129
130
  "@octokit/rest": "^22.0.0",
130
131
  "azure-devops-node-api": "^15.1.1",
131
132
  "chalk": "4.1.2",