claude-autopm 1.17.0 ā 1.20.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/README.md +159 -0
- package/autopm/.claude/agents/core/mcp-manager.md +1 -1
- package/autopm/.claude/commands/pm/context.md +11 -0
- package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
- package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
- package/autopm/.claude/commands/pm/epic-start.md +19 -0
- package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
- package/autopm/.claude/commands/pm/epic-sync.md +14 -14
- package/autopm/.claude/commands/pm/issue-start.md +50 -5
- package/autopm/.claude/commands/pm/issue-sync.md +15 -15
- package/autopm/.claude/commands/pm/what-next.md +11 -0
- package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
- package/autopm/.claude/scripts/azure/active-work.js +2 -2
- package/autopm/.claude/scripts/azure/blocked.js +13 -13
- package/autopm/.claude/scripts/azure/daily.js +1 -1
- package/autopm/.claude/scripts/azure/dashboard.js +1 -1
- package/autopm/.claude/scripts/azure/feature-list.js +2 -2
- package/autopm/.claude/scripts/azure/feature-status.js +1 -1
- package/autopm/.claude/scripts/azure/next-task.js +1 -1
- package/autopm/.claude/scripts/azure/search.js +1 -1
- package/autopm/.claude/scripts/azure/setup.js +15 -15
- package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
- package/autopm/.claude/scripts/azure/sync.js +1 -1
- package/autopm/.claude/scripts/azure/us-list.js +1 -1
- package/autopm/.claude/scripts/azure/us-status.js +1 -1
- package/autopm/.claude/scripts/azure/validate.js +13 -13
- package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
- package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
- package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
- package/autopm/.claude/scripts/pm/context.js +338 -0
- package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
- package/autopm/.claude/scripts/pm/lib/README.md +85 -0
- package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
- package/autopm/.claude/scripts/pm/next.js +25 -1
- package/autopm/.claude/scripts/pm/what-next.js +660 -0
- package/bin/autopm.js +25 -0
- package/bin/commands/team.js +86 -0
- package/package.json +1 -1
- package/lib/agentExecutor.js.deprecated +0 -101
- package/lib/azure/cache.js +0 -80
- package/lib/azure/client.js +0 -77
- package/lib/azure/formatter.js +0 -177
- package/lib/commandHelpers.js +0 -177
- package/lib/context/manager.js +0 -290
- package/lib/documentation/manager.js +0 -528
- package/lib/github/workflow-manager.js +0 -546
- package/lib/helpers/azure-batch-api.js +0 -133
- package/lib/helpers/azure-cache-manager.js +0 -287
- package/lib/helpers/azure-parallel-processor.js +0 -158
- package/lib/helpers/azure-work-item-create.js +0 -278
- package/lib/helpers/gh-issue-create.js +0 -250
- package/lib/helpers/interactive-prompt.js +0 -336
- package/lib/helpers/output-manager.js +0 -335
- package/lib/helpers/progress-indicator.js +0 -258
- package/lib/performance/benchmarker.js +0 -429
- package/lib/pm/epic-decomposer.js +0 -273
- package/lib/pm/epic-syncer.js +0 -221
- package/lib/prdMetadata.js +0 -270
- package/lib/providers/azure/index.js +0 -234
- package/lib/providers/factory.js +0 -87
- package/lib/providers/github/index.js +0 -204
- package/lib/providers/interface.js +0 -73
- package/lib/python/scaffold-manager.js +0 -576
- package/lib/react/scaffold-manager.js +0 -745
- package/lib/regression/analyzer.js +0 -578
- package/lib/release/manager.js +0 -324
- package/lib/tailwind/manager.js +0 -486
- package/lib/traefik/manager.js +0 -484
- package/lib/utils/colors.js +0 -126
- package/lib/utils/config.js +0 -317
- package/lib/utils/filesystem.js +0 -316
- package/lib/utils/logger.js +0 -135
- package/lib/utils/prompts.js +0 -294
- package/lib/utils/shell.js +0 -237
- package/lib/validators/email-validator.js +0 -337
- package/lib/workflow/manager.js +0 -449
package/bin/commands/team.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const MCPHandler = require('../../scripts/mcp-handler.js');
|
|
5
6
|
|
|
6
7
|
// Paths
|
|
7
8
|
const projectRoot = process.cwd();
|
|
@@ -68,6 +69,69 @@ function validateAgentFiles(agents, projectRoot) {
|
|
|
68
69
|
return missingAgents;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
// Helper function to validate MCP dependencies for agents
|
|
73
|
+
function validateAgentMCPDependencies(agents, projectRoot) {
|
|
74
|
+
const warnings = [];
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const mcpHandler = new MCPHandler();
|
|
78
|
+
const config = mcpHandler.loadConfig();
|
|
79
|
+
const activeServers = config.mcp?.activeServers || [];
|
|
80
|
+
|
|
81
|
+
// Track all required MCP servers across all agents
|
|
82
|
+
const requiredServers = new Map(); // server -> [agents that need it]
|
|
83
|
+
|
|
84
|
+
agents.forEach(agentPath => {
|
|
85
|
+
// Extract agent name from path
|
|
86
|
+
// Handle both "core/test-runner.md" and "test-runner.md" formats
|
|
87
|
+
let agentName = agentPath;
|
|
88
|
+
if (agentName.endsWith('.md')) {
|
|
89
|
+
agentName = agentName.slice(0, -3); // Remove .md extension
|
|
90
|
+
}
|
|
91
|
+
// Get just the filename without directory
|
|
92
|
+
agentName = path.basename(agentName);
|
|
93
|
+
|
|
94
|
+
// Get MCP dependencies for this agent
|
|
95
|
+
const agentMCP = mcpHandler.getAgentMCP(agentName);
|
|
96
|
+
|
|
97
|
+
if (agentMCP.found && agentMCP.mcpServers.length > 0) {
|
|
98
|
+
agentMCP.mcpServers.forEach(serverName => {
|
|
99
|
+
if (!requiredServers.has(serverName)) {
|
|
100
|
+
requiredServers.set(serverName, []);
|
|
101
|
+
}
|
|
102
|
+
requiredServers.get(serverName).push(agentName);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Check which required servers are missing or inactive
|
|
108
|
+
requiredServers.forEach((agentNames, serverName) => {
|
|
109
|
+
const isActive = activeServers.includes(serverName);
|
|
110
|
+
const serverExists = mcpHandler.getServer(serverName);
|
|
111
|
+
|
|
112
|
+
if (!serverExists) {
|
|
113
|
+
warnings.push({
|
|
114
|
+
type: 'not_installed',
|
|
115
|
+
server: serverName,
|
|
116
|
+
agents: agentNames
|
|
117
|
+
});
|
|
118
|
+
} else if (!isActive) {
|
|
119
|
+
warnings.push({
|
|
120
|
+
type: 'not_active',
|
|
121
|
+
server: serverName,
|
|
122
|
+
agents: agentNames
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
} catch (error) {
|
|
128
|
+
// If MCP validation fails, just warn but don't block
|
|
129
|
+
console.warn('ā ļø Warning: Could not validate MCP dependencies');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return warnings;
|
|
133
|
+
}
|
|
134
|
+
|
|
71
135
|
// Helper function to generate agent include list in Markdown format
|
|
72
136
|
function generateAgentIncludes(agents) {
|
|
73
137
|
return agents
|
|
@@ -216,6 +280,28 @@ const commands = {
|
|
|
216
280
|
});
|
|
217
281
|
}
|
|
218
282
|
|
|
283
|
+
// Validate MCP dependencies
|
|
284
|
+
const mcpWarnings = validateAgentMCPDependencies(agents, projectRoot);
|
|
285
|
+
if (mcpWarnings.length > 0) {
|
|
286
|
+
console.warn('\nā ļø MCP Dependency Warnings:\n');
|
|
287
|
+
|
|
288
|
+
mcpWarnings.forEach(warning => {
|
|
289
|
+
if (warning.type === 'not_installed') {
|
|
290
|
+
console.warn(`ā MCP server '${warning.server}' is NOT INSTALLED`);
|
|
291
|
+
console.warn(` Required by: ${warning.agents.join(', ')}`);
|
|
292
|
+
console.warn(` Fix: autopm mcp install ${warning.server}`);
|
|
293
|
+
} else if (warning.type === 'not_active') {
|
|
294
|
+
console.warn(`āŖ MCP server '${warning.server}' is NOT ACTIVE`);
|
|
295
|
+
console.warn(` Required by: ${warning.agents.join(', ')}`);
|
|
296
|
+
console.warn(` Fix: autopm mcp enable ${warning.server}`);
|
|
297
|
+
}
|
|
298
|
+
console.warn('');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
console.warn('š” Tip: Run "autopm mcp list" to see all MCP servers');
|
|
302
|
+
console.warn('š” Tip: Run "autopm mcp setup" for interactive configuration\n');
|
|
303
|
+
}
|
|
304
|
+
|
|
219
305
|
// Update CLAUDE.md with the new agent list
|
|
220
306
|
updateClaudeMd(agents);
|
|
221
307
|
console.log('ā Updated CLAUDE.md with team agents');
|
package/package.json
CHANGED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Executor Module
|
|
3
|
-
* Handles execution of AI agents with prompts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { spawn } = require('child_process');
|
|
9
|
-
const colors = require('./utils/colors');
|
|
10
|
-
|
|
11
|
-
class AgentExecutor {
|
|
12
|
-
constructor() {
|
|
13
|
-
this.agentsPath = path.join(__dirname, '../autopm/.claude/agents');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Execute an agent with the given prompt
|
|
18
|
-
* @param {string} agentType - The type of agent to execute
|
|
19
|
-
* @param {string} prompt - The prompt to send to the agent
|
|
20
|
-
* @param {Object} context - Additional context for the agent
|
|
21
|
-
* @returns {Promise<Object>} The result of the agent execution
|
|
22
|
-
*/
|
|
23
|
-
async run(agentType, prompt, context = {}) {
|
|
24
|
-
console.log(colors.blue(`š¤ Preparing ${agentType} agent...`));
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
// Replace $ARGUMENTS in prompt with actual arguments
|
|
28
|
-
let finalPrompt = prompt;
|
|
29
|
-
if (context.arguments) {
|
|
30
|
-
finalPrompt = prompt.replace(/\$ARGUMENTS/g, context.arguments);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Check if we're in Claude Code environment
|
|
34
|
-
const isClaudeCode = process.env.CLAUDE_CODE === 'true' ||
|
|
35
|
-
process.env.ANTHROPIC_WORKSPACE === 'true';
|
|
36
|
-
|
|
37
|
-
if (isClaudeCode) {
|
|
38
|
-
// If in Claude Code, use the Task tool
|
|
39
|
-
console.log(colors.cyan('š Executing via Claude Code Task tool...'));
|
|
40
|
-
console.log(colors.gray('Prompt:'), finalPrompt.substring(0, 200) + '...');
|
|
41
|
-
|
|
42
|
-
// Claude Code should handle this through its Task tool
|
|
43
|
-
// This would be invoked by Claude when running the command
|
|
44
|
-
return {
|
|
45
|
-
status: 'success',
|
|
46
|
-
agent: agentType,
|
|
47
|
-
message: 'Agent should be executed via Claude Code Task tool',
|
|
48
|
-
prompt: finalPrompt,
|
|
49
|
-
context: context
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Not in Claude Code - show instructions
|
|
54
|
-
console.log();
|
|
55
|
-
console.log(colors.yellow('ā ļø This command requires Claude Code'));
|
|
56
|
-
console.log();
|
|
57
|
-
console.log(colors.cyan('To use AI-powered commands:'));
|
|
58
|
-
console.log();
|
|
59
|
-
console.log(colors.green('1. Open your project in Claude Code'));
|
|
60
|
-
console.log(colors.gray('2. Run commands directly in chat:'));
|
|
61
|
-
console.log(colors.gray(` /pm:prd-new ${context.arguments || 'feature-name'}`));
|
|
62
|
-
console.log();
|
|
63
|
-
console.log(colors.blue('Why Claude Code?'));
|
|
64
|
-
console.log(colors.gray('⢠Interactive brainstorming sessions'));
|
|
65
|
-
console.log(colors.gray('⢠Intelligent context understanding'));
|
|
66
|
-
console.log(colors.gray('⢠Real-time Q&A during PRD creation'));
|
|
67
|
-
console.log();
|
|
68
|
-
console.log(colors.cyan('š What this command would do:'));
|
|
69
|
-
console.log(colors.gray('ā'.repeat(60)));
|
|
70
|
-
|
|
71
|
-
// Show a summary of the command intent
|
|
72
|
-
if (agentType === 'pm-specialist' && context.arguments) {
|
|
73
|
-
console.log(colors.white(`Create PRD for: ${context.arguments}`));
|
|
74
|
-
console.log();
|
|
75
|
-
console.log('The AI agent would:');
|
|
76
|
-
console.log('1. Ask clarifying questions about the feature');
|
|
77
|
-
console.log('2. Help define user stories and requirements');
|
|
78
|
-
console.log('3. Generate comprehensive PRD document');
|
|
79
|
-
console.log(`4. Save to: .claude/prds/${context.arguments}.md`);
|
|
80
|
-
} else {
|
|
81
|
-
console.log(finalPrompt.substring(0, 300) + '...');
|
|
82
|
-
}
|
|
83
|
-
console.log(colors.gray('ā'.repeat(60)));
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
status: 'claude-required',
|
|
87
|
-
agent: agentType,
|
|
88
|
-
message: 'Claude Code required for AI-powered commands',
|
|
89
|
-
prompt: finalPrompt,
|
|
90
|
-
context: context
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error(colors.red(`ā Agent execution failed: ${error.message}`));
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
module.exports = new AgentExecutor();
|
package/lib/azure/cache.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple cache implementation for Azure DevOps API responses
|
|
3
|
-
* Uses LRU (Least Recently Used) eviction policy
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class AzureCache {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
this.ttl = options.ttl || 5 * 60 * 1000; // 5 minutes default
|
|
9
|
-
this.maxSize = options.maxSize || 100;
|
|
10
|
-
this.cache = new Map();
|
|
11
|
-
this.stats = { hits: 0, misses: 0 };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get(key) {
|
|
15
|
-
const item = this.cache.get(key);
|
|
16
|
-
|
|
17
|
-
if (!item) {
|
|
18
|
-
this.stats.misses++;
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (this.isExpired(item)) {
|
|
23
|
-
this.cache.delete(key);
|
|
24
|
-
this.stats.misses++;
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Move to end (most recently used)
|
|
29
|
-
this.cache.delete(key);
|
|
30
|
-
this.cache.set(key, item);
|
|
31
|
-
|
|
32
|
-
this.stats.hits++;
|
|
33
|
-
return item.value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
set(key, value) {
|
|
37
|
-
// Remove oldest item if at capacity
|
|
38
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
39
|
-
const firstKey = this.cache.keys().next().value;
|
|
40
|
-
this.cache.delete(firstKey);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.cache.set(key, {
|
|
44
|
-
value,
|
|
45
|
-
timestamp: Date.now()
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
isExpired(item) {
|
|
50
|
-
return Date.now() - item.timestamp > this.ttl;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
clear() {
|
|
54
|
-
this.cache.clear();
|
|
55
|
-
this.stats = { hits: 0, misses: 0 };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
getStats() {
|
|
59
|
-
const total = this.stats.hits + this.stats.misses;
|
|
60
|
-
const hitRate = total > 0 ? (this.stats.hits / total * 100).toFixed(2) : 0;
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
...this.stats,
|
|
64
|
-
hitRate: `${hitRate}%`,
|
|
65
|
-
size: this.cache.size,
|
|
66
|
-
maxSize: this.maxSize
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Clean up expired entries
|
|
71
|
-
cleanup() {
|
|
72
|
-
for (const [key, item] of this.cache.entries()) {
|
|
73
|
-
if (this.isExpired(item)) {
|
|
74
|
-
this.cache.delete(key);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
module.exports = AzureCache;
|
package/lib/azure/client.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Azure DevOps Client
|
|
5
|
-
* Wrapper for Azure DevOps REST API
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
class AzureDevOpsClient {
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
this.organization = options.organization;
|
|
11
|
-
this.project = options.project;
|
|
12
|
-
this.token = options.token;
|
|
13
|
-
this.baseUrl = `https://dev.azure.com/${this.organization}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Create work item
|
|
18
|
-
*/
|
|
19
|
-
async createWorkItem(options) {
|
|
20
|
-
// This would make actual API call
|
|
21
|
-
console.log(`Creating ${options.type} work item`);
|
|
22
|
-
return {
|
|
23
|
-
id: Math.floor(Math.random() * 10000),
|
|
24
|
-
fields: options.fields,
|
|
25
|
-
url: `${this.baseUrl}/${this.project}/_workitems/edit/${Math.floor(Math.random() * 10000)}`
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Update work item
|
|
31
|
-
*/
|
|
32
|
-
async updateWorkItem(options) {
|
|
33
|
-
console.log(`Updating work item ${options.id}`);
|
|
34
|
-
return {
|
|
35
|
-
id: options.id,
|
|
36
|
-
fields: options.fields
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get work item
|
|
42
|
-
*/
|
|
43
|
-
async getWorkItem(options) {
|
|
44
|
-
console.log(`Getting work item ${options.id}`);
|
|
45
|
-
return {
|
|
46
|
-
id: options.id,
|
|
47
|
-
fields: {
|
|
48
|
-
'System.Title': 'Work Item',
|
|
49
|
-
'System.State': 'Active'
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Create client instance
|
|
57
|
-
*/
|
|
58
|
-
function createClient(options) {
|
|
59
|
-
return new AzureDevOpsClient(options);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get default client
|
|
64
|
-
*/
|
|
65
|
-
function getClient() {
|
|
66
|
-
return createClient({
|
|
67
|
-
organization: process.env.AZURE_DEVOPS_ORG,
|
|
68
|
-
project: process.env.AZURE_DEVOPS_PROJECT,
|
|
69
|
-
token: process.env.AZURE_DEVOPS_PAT
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
module.exports = {
|
|
74
|
-
AzureDevOpsClient,
|
|
75
|
-
createClient,
|
|
76
|
-
getClient
|
|
77
|
-
};
|
package/lib/azure/formatter.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Azure DevOps Output Formatter
|
|
3
|
-
* Formats work items and sprint data for console display
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const Table = require('table').table;
|
|
7
|
-
const chalk = require('chalk');
|
|
8
|
-
|
|
9
|
-
class AzureFormatter {
|
|
10
|
-
static formatWorkItems(items, options = {}) {
|
|
11
|
-
if (!items || items.length === 0) {
|
|
12
|
-
return chalk.yellow('No work items found.');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const headers = this.getHeaders(options);
|
|
16
|
-
const rows = items.map(item => this.formatWorkItem(item, options));
|
|
17
|
-
|
|
18
|
-
const tableConfig = {
|
|
19
|
-
header: {
|
|
20
|
-
alignment: 'center',
|
|
21
|
-
content: chalk.cyan.bold('Work Items')
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return Table([headers, ...rows], tableConfig);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
static getHeaders(options) {
|
|
29
|
-
const headers = [
|
|
30
|
-
chalk.bold('ID'),
|
|
31
|
-
chalk.bold('Type'),
|
|
32
|
-
chalk.bold('Title'),
|
|
33
|
-
chalk.bold('State'),
|
|
34
|
-
chalk.bold('Assigned To')
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
if (options.showRemaining) {
|
|
38
|
-
headers.push(chalk.bold('Remaining'));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (options.showPriority) {
|
|
42
|
-
headers.push(chalk.bold('Priority'));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return headers;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
static formatWorkItem(item, options) {
|
|
49
|
-
const fields = item.fields || {};
|
|
50
|
-
const row = [
|
|
51
|
-
chalk.blue(item.id),
|
|
52
|
-
this.getTypeIcon(fields['System.WorkItemType']),
|
|
53
|
-
this.truncate(fields['System.Title'], 50),
|
|
54
|
-
this.getStateColor(fields['System.State']),
|
|
55
|
-
this.getAssignedTo(fields['System.AssignedTo'])
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
if (options.showRemaining) {
|
|
59
|
-
row.push(fields['Microsoft.VSTS.Scheduling.RemainingWork'] || '-');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (options.showPriority) {
|
|
63
|
-
row.push(fields['Microsoft.VSTS.Common.Priority'] || '-');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return row;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
static getTypeIcon(type) {
|
|
70
|
-
const icons = {
|
|
71
|
-
'Task': 'š Task',
|
|
72
|
-
'Bug': 'š Bug',
|
|
73
|
-
'User Story': 'š Story',
|
|
74
|
-
'Feature': 'šÆ Feature',
|
|
75
|
-
'Epic': 'šļø Epic'
|
|
76
|
-
};
|
|
77
|
-
return icons[type] || type;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static getStateColor(state) {
|
|
81
|
-
const colors = {
|
|
82
|
-
'New': chalk.gray(state),
|
|
83
|
-
'Active': chalk.blue(state),
|
|
84
|
-
'In Progress': chalk.yellow(state),
|
|
85
|
-
'Resolved': chalk.green(state),
|
|
86
|
-
'Closed': chalk.dim(state),
|
|
87
|
-
'Done': chalk.green(state),
|
|
88
|
-
'Removed': chalk.strikethrough(state)
|
|
89
|
-
};
|
|
90
|
-
return colors[state] || state;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
static getAssignedTo(assignedTo) {
|
|
94
|
-
if (!assignedTo) return chalk.gray('Unassigned');
|
|
95
|
-
const name = assignedTo.displayName || assignedTo.uniqueName || assignedTo;
|
|
96
|
-
return this.truncate(name, 20);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static truncate(text, length) {
|
|
100
|
-
if (!text) return '';
|
|
101
|
-
if (text.length <= length) return text;
|
|
102
|
-
return text.substring(0, length - 3) + '...';
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
static formatSummary(items) {
|
|
106
|
-
if (!items || items.length === 0) {
|
|
107
|
-
return '';
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const states = {};
|
|
111
|
-
const types = {};
|
|
112
|
-
let totalRemaining = 0;
|
|
113
|
-
|
|
114
|
-
items.forEach(item => {
|
|
115
|
-
const fields = item.fields || {};
|
|
116
|
-
const state = fields['System.State'];
|
|
117
|
-
const type = fields['System.WorkItemType'];
|
|
118
|
-
const remaining = fields['Microsoft.VSTS.Scheduling.RemainingWork'] || 0;
|
|
119
|
-
|
|
120
|
-
states[state] = (states[state] || 0) + 1;
|
|
121
|
-
types[type] = (types[type] || 0) + 1;
|
|
122
|
-
totalRemaining += remaining;
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const lines = [];
|
|
126
|
-
lines.push(chalk.bold('\nš Summary:'));
|
|
127
|
-
lines.push(`Total items: ${chalk.cyan(items.length)}`);
|
|
128
|
-
|
|
129
|
-
if (Object.keys(types).length > 0) {
|
|
130
|
-
lines.push('\nBy Type:');
|
|
131
|
-
Object.entries(types).forEach(([type, count]) => {
|
|
132
|
-
lines.push(` ${this.getTypeIcon(type)}: ${count}`);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (Object.keys(states).length > 0) {
|
|
137
|
-
lines.push('\nBy State:');
|
|
138
|
-
Object.entries(states).forEach(([state, count]) => {
|
|
139
|
-
lines.push(` ${this.getStateColor(state)}: ${count}`);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (totalRemaining > 0) {
|
|
144
|
-
lines.push(`\nTotal Remaining Work: ${chalk.yellow(totalRemaining + ' hours')}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return lines.join('\n');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
static formatSprintInfo(sprint) {
|
|
151
|
-
if (!sprint) {
|
|
152
|
-
return chalk.yellow('No active sprint found.');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const attributes = sprint.attributes || {};
|
|
156
|
-
const startDate = attributes.startDate ? new Date(attributes.startDate).toLocaleDateString() : 'N/A';
|
|
157
|
-
const endDate = attributes.finishDate ? new Date(attributes.finishDate).toLocaleDateString() : 'N/A';
|
|
158
|
-
|
|
159
|
-
return chalk.cyan.bold(`\nš Current Sprint: ${sprint.name}\n`) +
|
|
160
|
-
` Start: ${startDate}\n` +
|
|
161
|
-
` End: ${endDate}\n`;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static formatError(error) {
|
|
165
|
-
return chalk.red(`\nā Error: ${error.message}\n`);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
static formatSuccess(message) {
|
|
169
|
-
return chalk.green(`\nā
${message}\n`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
static formatWarning(message) {
|
|
173
|
-
return chalk.yellow(`\nā ļø ${message}\n`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
module.exports = AzureFormatter;
|