den-github-manager 1.0.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,78 @@
1
+ # Product Backlog
2
+
3
+ Generated: 2026-02-06
4
+
5
+ ## Milestones
6
+
7
+ | ID | Milestone | Objective | State |
8
+ |----|-----------|-----------|-------|
9
+ | M0 | Foundation | Setup and initial configuration | ✅ closed |
10
+ | M1 | Core Features | Main functionality | ⬜ open |
11
+ | M2 | Polish | Testing and refinement | ⬜ open |
12
+
13
+ ---
14
+
15
+ ## Epics
16
+
17
+ | ID | Epic | Description | Milestone |
18
+ |----|------|-------------|-----------||
19
+ | E1 | Setup & Configuration | | M0 |
20
+ | E2 | Core Functionality | | M1 |
21
+ | E3 | User Interface | | M1 |
22
+
23
+ ---
24
+
25
+ ## User Stories
26
+
27
+ ### US-E1-01: Setup repository
28
+
29
+ **Priority**: P0 | **Points**: 3 | **Status**: ✅ completed
30
+
31
+ **Epic**: E1 | **Milestone**: M0
32
+
33
+ **Criterios de Aceptación**:
34
+ - [ ] Implement functionality
35
+ - [ ] Add tests
36
+ - [ ] Update documentation
37
+
38
+ ---
39
+
40
+ ### US-E1-02: Configure development environment
41
+
42
+ **Priority**: P0 | **Points**: 5 | **Status**: ✅ completed
43
+
44
+ **Epic**: E1 | **Milestone**: M0
45
+
46
+ **Criterios de Aceptación**:
47
+ - [ ] Implement functionality
48
+ - [ ] Add tests
49
+ - [ ] Update documentation
50
+
51
+ ---
52
+
53
+ ### US-E2-01: Implement core logic
54
+
55
+ **Priority**: P0 | **Points**: 8 | **Status**: ⬜ pending
56
+
57
+ **Epic**: E2 | **Milestone**: M1
58
+
59
+ **Criterios de Aceptación**:
60
+ - [ ] Implement functionality
61
+ - [ ] Add tests
62
+ - [ ] Update documentation
63
+
64
+ ---
65
+
66
+ ### US-E3-01: Create main UI components
67
+
68
+ **Priority**: P1 | **Points**: 5 | **Status**: ⬜ pending
69
+
70
+ **Epic**: E3 | **Milestone**: M1
71
+
72
+ **Criterios de Aceptación**:
73
+ - [ ] Implement functionality
74
+ - [ ] Add tests
75
+ - [ ] Update documentation
76
+
77
+ ---
78
+
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "den-github-manager",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to setup GitHub Projects automatically - Analyze, generate backlog, and configure milestones, issues, and project boards in minutes",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "den": "./bin/den.js",
9
+ "den-github-manager": "./bin/den.js"
10
+ },
11
+ "scripts": {
12
+ "start": "node bin/den.js",
13
+ "test": "jest",
14
+ "lint": "eslint src/",
15
+ "format": "prettier --write \"src/**/*.js\"",
16
+ "prepare": "chmod +x bin/den.js"
17
+ },
18
+ "keywords": [
19
+ "github",
20
+ "project-management",
21
+ "cli",
22
+ "automation",
23
+ "kanban",
24
+ "agile",
25
+ "scrum",
26
+ "milestones",
27
+ "issues",
28
+ "project-board",
29
+ "backlog",
30
+ "devtools"
31
+ ],
32
+ "author": "wolfcito <@akawolfcito>",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/wolfcito/den-github-manager.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/wolfcito/den-github-manager/issues"
40
+ },
41
+ "homepage": "https://github.com/wolfcito/den-github-manager#readme",
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ },
45
+ "dependencies": {
46
+ "chalk": "^5.3.0",
47
+ "commander": "^11.1.0",
48
+ "inquirer": "^9.2.12",
49
+ "ora": "^7.0.1",
50
+ "@octokit/rest": "^20.0.2",
51
+ "js-yaml": "^4.1.0",
52
+ "marked": "^11.1.0"
53
+ },
54
+ "devDependencies": {
55
+ "eslint": "^8.55.0",
56
+ "jest": "^29.7.0",
57
+ "prettier": "^3.1.1"
58
+ }
59
+ }
@@ -0,0 +1,122 @@
1
+ #!/bin/bash
2
+
3
+ # Pre-publish test script
4
+ # Run this before publishing to npm
5
+
6
+ set -e
7
+
8
+ echo "🧪 Pre-Publish Testing"
9
+ echo "======================"
10
+ echo ""
11
+
12
+ # 1. Syntax check
13
+ echo "1️⃣ Checking syntax..."
14
+ node --check bin/den.js
15
+ echo "✅ Syntax OK"
16
+ echo ""
17
+
18
+ # 2. Check permissions
19
+ echo "2️⃣ Checking permissions..."
20
+ if [ -x "bin/den.js" ]; then
21
+ echo "✅ bin/den.js is executable"
22
+ else
23
+ echo "❌ bin/den.js is not executable"
24
+ echo "Run: chmod +x bin/den.js"
25
+ exit 1
26
+ fi
27
+ echo ""
28
+
29
+ # 3. Install dependencies
30
+ echo "3️⃣ Installing dependencies..."
31
+ npm ci > /dev/null 2>&1
32
+ echo "✅ Dependencies installed"
33
+ echo ""
34
+
35
+ # 4. Test basic commands
36
+ echo "4️⃣ Testing CLI commands..."
37
+ ./bin/den.js --version > /dev/null
38
+ echo "✅ Version command works"
39
+
40
+ ./bin/den.js --help > /dev/null
41
+ echo "✅ Help command works"
42
+
43
+ ./bin/den.js templates --list > /dev/null
44
+ echo "✅ Templates command works"
45
+
46
+ ./bin/den.js analyze > /dev/null 2>&1 || true
47
+ echo "✅ Analyze command works"
48
+
49
+ ./bin/den.js init --template simple --auto --dry-run > /dev/null 2>&1
50
+ echo "✅ Init command works (dry-run)"
51
+ echo ""
52
+
53
+ # 5. Check package
54
+ echo "5️⃣ Checking package contents..."
55
+ npm pack --dry-run > /tmp/pack-contents.txt 2>&1
56
+
57
+ EXPECTED_FILES=(
58
+ "package.json"
59
+ "README.md"
60
+ "LICENSE"
61
+ "bin/den.js"
62
+ "src/"
63
+ )
64
+
65
+ for file in "${EXPECTED_FILES[@]}"; do
66
+ if grep -q "$file" /tmp/pack-contents.txt; then
67
+ echo "✅ $file included"
68
+ else
69
+ echo "⚠️ $file might be missing"
70
+ fi
71
+ done
72
+
73
+ echo ""
74
+
75
+ # 6. Check package.json
76
+ echo "6️⃣ Validating package.json..."
77
+ required_fields=("name" "version" "description" "main" "bin" "author" "license")
78
+
79
+ for field in "${required_fields[@]}"; do
80
+ if grep -q "\"$field\"" package.json; then
81
+ echo "✅ $field present"
82
+ else
83
+ echo "❌ $field missing"
84
+ exit 1
85
+ fi
86
+ done
87
+
88
+ echo ""
89
+
90
+ # 7. Size check
91
+ echo "7️⃣ Package size check..."
92
+ npm pack --quiet > /dev/null
93
+ TARBALL=$(ls -1 den-github-manager-*.tgz 2>/dev/null | head -1)
94
+
95
+ if [ -f "$TARBALL" ]; then
96
+ SIZE=$(du -h "$TARBALL" | cut -f1)
97
+ echo "📦 Package size: $SIZE"
98
+
99
+ # Clean up
100
+ rm "$TARBALL"
101
+
102
+ # Warn if > 1MB
103
+ SIZE_KB=$(du -k "$TARBALL" 2>/dev/null | cut -f1 || echo 0)
104
+ if [ $SIZE_KB -gt 1024 ]; then
105
+ echo "⚠️ Package is larger than 1MB"
106
+ fi
107
+ else
108
+ echo "⚠️ Could not determine package size"
109
+ fi
110
+
111
+ echo ""
112
+
113
+ # Summary
114
+ echo "✅ All pre-publish tests passed!"
115
+ echo ""
116
+ echo "📋 Next steps:"
117
+ echo " 1. Commit all changes: git add . && git commit"
118
+ echo " 2. Push to GitHub: git push"
119
+ echo " 3. Login to npm: npm login"
120
+ echo " 4. Publish: npm publish"
121
+ echo " 5. Create release: gh release create v1.0.0"
122
+ echo ""
@@ -0,0 +1,35 @@
1
+ import { Analyzer } from '../core/analyzer.js';
2
+ import { Logger } from '../utils/logger.js';
3
+
4
+ export async function analyzeCommand(options) {
5
+ const logger = new Logger();
6
+ const analyzer = new Analyzer();
7
+
8
+ logger.info('Analyzing project...');
9
+
10
+ const analysis = await analyzer.analyze();
11
+
12
+ if (options.json) {
13
+ logger.json(analysis);
14
+ } else {
15
+ analyzer.generateReport(analysis);
16
+
17
+ // Additional insights
18
+ logger.newline();
19
+ logger.section('💡 Recommendations');
20
+
21
+ if (!analysis.hasBacklog.exists) {
22
+ logger.info('Consider creating a backlog with: den init');
23
+ }
24
+
25
+ if (!analysis.hasDocumentation.exists) {
26
+ logger.info('Consider adding documentation in docs/');
27
+ }
28
+
29
+ if (analysis.phase === 'new') {
30
+ logger.info('Perfect time to set up project management!');
31
+ }
32
+
33
+ logger.newline();
34
+ }
35
+ }
@@ -0,0 +1,42 @@
1
+ import { Config } from '../utils/config.js';
2
+ import { Logger } from '../utils/logger.js';
3
+
4
+ export async function configCommand(options) {
5
+ const logger = new Logger();
6
+ const config = new Config();
7
+
8
+ await config.load();
9
+
10
+ if (options.set) {
11
+ const [key, value] = options.set.split('=');
12
+
13
+ if (!key || !value) {
14
+ logger.error('Invalid format. Use: --set key=value');
15
+ return;
16
+ }
17
+
18
+ config.set(key, value);
19
+ await config.save();
20
+ logger.success(`Set ${key} = ${value}`);
21
+ }
22
+
23
+ if (options.get) {
24
+ const value = config.get(options.get);
25
+ console.log(value);
26
+ }
27
+
28
+ if (options.list || (!options.set && !options.get)) {
29
+ logger.section('⚙️ Configuration');
30
+ logger.newline();
31
+
32
+ const allConfig = config.getAll();
33
+
34
+ for (const [key, value] of Object.entries(allConfig)) {
35
+ logger.info(`${key}: ${JSON.stringify(value)}`);
36
+ }
37
+
38
+ logger.newline();
39
+ logger.dim('Config file: ~/.den-github-manager/config.json');
40
+ logger.newline();
41
+ }
42
+ }
@@ -0,0 +1,224 @@
1
+ import inquirer from 'inquirer';
2
+ import ora from 'ora';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { Analyzer } from '../core/analyzer.js';
6
+ import { Generator } from '../core/generator.js';
7
+ import { GitHubClient } from '../core/github-client.js';
8
+ import { Logger } from '../utils/logger.js';
9
+
10
+ export async function initCommand(options) {
11
+ const logger = new Logger();
12
+
13
+ logger.section('🦁 Den GitHub Manager - Project Setup');
14
+
15
+ // Step 1: Analyze project
16
+ const analyzer = new Analyzer();
17
+ const spinner = ora('Analyzing project...').start();
18
+
19
+ let analysis;
20
+ try {
21
+ analysis = await analyzer.analyze();
22
+ spinner.succeed('Project analyzed');
23
+ } catch (error) {
24
+ spinner.fail('Analysis failed');
25
+ logger.error(error.message);
26
+ return;
27
+ }
28
+
29
+ // Show analysis report
30
+ if (!options.auto) {
31
+ logger.newline();
32
+ analyzer.generateReport(analysis);
33
+ logger.newline();
34
+ }
35
+
36
+ // Step 2: Determine backlog strategy
37
+ let backlogContent;
38
+ let template = options.template || 'simple';
39
+
40
+ if (analysis.hasBacklog.exists) {
41
+ logger.success(`Found existing backlog: ${analysis.hasBacklog.path}`);
42
+
43
+ if (!options.auto) {
44
+ const { useExisting } = await inquirer.prompt([{
45
+ type: 'confirm',
46
+ name: 'useExisting',
47
+ message: 'Use existing backlog?',
48
+ default: true
49
+ }]);
50
+
51
+ if (!useExisting) {
52
+ template = await selectTemplate();
53
+ } else {
54
+ // Read existing backlog
55
+ const backlogPath = path.join(process.cwd(), analysis.hasBacklog.path);
56
+ backlogContent = await fs.readFile(backlogPath, 'utf-8');
57
+ }
58
+ }
59
+ } else {
60
+ logger.warn('No backlog found');
61
+
62
+ if (!options.auto) {
63
+ const { action } = await inquirer.prompt([{
64
+ type: 'list',
65
+ name: 'action',
66
+ message: 'How would you like to proceed?',
67
+ choices: [
68
+ { name: '📋 Use a template (recommended)', value: 'template' },
69
+ { name: '🤖 Generate from project structure', value: 'generate' },
70
+ { name: '✍️ Create manually later', value: 'skip' }
71
+ ]
72
+ }]);
73
+
74
+ if (action === 'template') {
75
+ template = await selectTemplate();
76
+ } else if (action === 'skip') {
77
+ logger.info('Skipping backlog generation. You can create it manually later.');
78
+ return;
79
+ }
80
+ }
81
+ }
82
+
83
+ // Step 3: Generate or parse backlog
84
+ const generator = new Generator();
85
+
86
+ if (!backlogContent) {
87
+ spinner.start('Generating backlog...');
88
+ backlogContent = await generator.generateBacklog(analysis, template);
89
+ spinner.succeed(`Backlog generated using "${template}" template`);
90
+
91
+ // Save backlog
92
+ const backlogPath = path.join(process.cwd(), 'docs', 'backlog.md');
93
+ await fs.mkdir(path.dirname(backlogPath), { recursive: true });
94
+ await fs.writeFile(backlogPath, backlogContent);
95
+ logger.success(`Saved to: docs/backlog.md`);
96
+ }
97
+
98
+ // Step 4: Parse backlog to extract structure
99
+ const backlog = {
100
+ milestones: generator.generateMilestones(analysis, template),
101
+ stories: generator.generateStories(analysis, template)
102
+ };
103
+
104
+ // Step 5: Confirm before creating on GitHub
105
+ logger.newline();
106
+ logger.section('📦 GitHub Setup Plan');
107
+ logger.info(`Milestones to create: ${backlog.milestones.length}`);
108
+ logger.info(`Issues to create: ${backlog.stories.length}`);
109
+ logger.info(`Labels: Priority, Status, Story Points, Epics`);
110
+ logger.newline();
111
+
112
+ if (options.dryRun) {
113
+ logger.warn('DRY RUN - No changes will be made');
114
+ logger.json({ backlog });
115
+ return;
116
+ }
117
+
118
+ if (!options.auto) {
119
+ const { proceed } = await inquirer.prompt([{
120
+ type: 'confirm',
121
+ name: 'proceed',
122
+ message: 'Proceed with GitHub setup?',
123
+ default: true
124
+ }]);
125
+
126
+ if (!proceed) {
127
+ logger.info('Setup cancelled');
128
+ return;
129
+ }
130
+ }
131
+
132
+ // Step 6: Initialize GitHub client
133
+ const github = new GitHubClient();
134
+
135
+ spinner.start('Initializing GitHub client...');
136
+ const initialized = await github.init();
137
+
138
+ if (!initialized) {
139
+ spinner.fail('GitHub initialization failed');
140
+ return;
141
+ }
142
+
143
+ spinner.succeed('GitHub client ready');
144
+
145
+ // Get repo info
146
+ const repoInfo = await github.getRepoInfo();
147
+ if (repoInfo) {
148
+ logger.info(`Repository: ${repoInfo.fullName}`);
149
+ logger.newline();
150
+ }
151
+
152
+ // Step 7: Create milestones
153
+ spinner.start('Creating milestones...');
154
+ try {
155
+ await github.createMilestones(backlog.milestones);
156
+ spinner.succeed(`Created ${backlog.milestones.length} milestones`);
157
+ } catch (error) {
158
+ spinner.fail('Milestone creation failed');
159
+ logger.error(error.message);
160
+ }
161
+
162
+ // Step 8: Create labels
163
+ spinner.start('Creating labels...');
164
+ try {
165
+ const epicLabels = (analysis.techStack || []).map((tech, i) => ({
166
+ name: `Epic:E${i + 1}-${tech}`,
167
+ color: '0e8a16'
168
+ }));
169
+
170
+ await github.createLabels(epicLabels);
171
+ spinner.succeed('Labels created');
172
+ } catch (error) {
173
+ spinner.fail('Label creation failed');
174
+ logger.error(error.message);
175
+ }
176
+
177
+ // Step 9: Create issues
178
+ spinner.start('Creating issues...');
179
+ try {
180
+ await github.createIssues(backlog.stories, backlog.milestones);
181
+ spinner.succeed(`Created ${backlog.stories.length} issues`);
182
+ } catch (error) {
183
+ spinner.fail('Issue creation failed');
184
+ logger.error(error.message);
185
+ }
186
+
187
+ // Step 10: Success summary
188
+ logger.newline();
189
+ logger.section('✅ Setup Complete!');
190
+ logger.newline();
191
+
192
+ if (repoInfo) {
193
+ logger.info(`📊 Milestones: ${repoInfo.url}/milestones`);
194
+ logger.info(`📝 Issues: ${repoInfo.url}/issues`);
195
+ logger.newline();
196
+ }
197
+
198
+ logger.info('💡 Next steps:');
199
+ logger.dim(' 1. Create a GitHub Project Board manually');
200
+ logger.dim(' 2. Add all issues to the project');
201
+ logger.dim(' 3. Configure project views');
202
+ logger.newline();
203
+
204
+ logger.info('🎯 Or run:');
205
+ logger.dim(' $ gh project create --owner <user> --title "<Name>"');
206
+ logger.dim(' $ den add-to-project <project-number>');
207
+ logger.newline();
208
+ }
209
+
210
+ async function selectTemplate() {
211
+ const { template } = await inquirer.prompt([{
212
+ type: 'list',
213
+ name: 'template',
214
+ message: 'Select a template:',
215
+ choices: [
216
+ { name: '📦 Simple (3 milestones, ~10 issues)', value: 'simple' },
217
+ { name: '🚀 Startup MVP (4 milestones, ~30 issues)', value: 'startup' },
218
+ { name: '🏃 Agile/Scrum (Sprint-based)', value: 'agile' },
219
+ { name: '🏢 Enterprise (5+ phases)', value: 'enterprise' }
220
+ ]
221
+ }]);
222
+
223
+ return template;
224
+ }
@@ -0,0 +1,71 @@
1
+ import { Logger } from '../utils/logger.js';
2
+
3
+ const TEMPLATES = {
4
+ simple: {
5
+ name: 'Simple',
6
+ description: 'Basic 3-milestone structure for small projects',
7
+ milestones: 3,
8
+ issues: '~10',
9
+ bestFor: 'Personal projects, side projects'
10
+ },
11
+ startup: {
12
+ name: 'Startup MVP',
13
+ description: 'MVP-focused with 4 milestones',
14
+ milestones: 4,
15
+ issues: '~30',
16
+ bestFor: 'Startups, product development'
17
+ },
18
+ agile: {
19
+ name: 'Agile/Scrum',
20
+ description: 'Sprint-based with recurring milestones',
21
+ milestones: 'Flexible (sprints)',
22
+ issues: '~50',
23
+ bestFor: 'Teams using Scrum/Agile'
24
+ },
25
+ enterprise: {
26
+ name: 'Enterprise',
27
+ description: 'Comprehensive 5+ phase structure',
28
+ milestones: '5+',
29
+ issues: '50+',
30
+ bestFor: 'Large organizations, complex projects'
31
+ }
32
+ };
33
+
34
+ export async function templatesCommand(options) {
35
+ const logger = new Logger();
36
+
37
+ if (options.list || !options.show) {
38
+ logger.section('📋 Available Templates');
39
+ logger.newline();
40
+
41
+ for (const [key, template] of Object.entries(TEMPLATES)) {
42
+ logger.bold(`${template.name} (${key})`);
43
+ logger.dim(` ${template.description}`);
44
+ logger.info(` Milestones: ${template.milestones} | Issues: ${template.issues}`);
45
+ logger.dim(` Best for: ${template.bestFor}`);
46
+ logger.newline();
47
+ }
48
+
49
+ logger.info('💡 Usage:');
50
+ logger.dim(' $ den init --template simple');
51
+ logger.dim(' $ den init --template startup');
52
+ logger.newline();
53
+ }
54
+
55
+ if (options.show) {
56
+ const template = TEMPLATES[options.show];
57
+
58
+ if (!template) {
59
+ logger.error(`Template "${options.show}" not found`);
60
+ return;
61
+ }
62
+
63
+ logger.section(`📋 Template: ${template.name}`);
64
+ logger.newline();
65
+ logger.info(`Description: ${template.description}`);
66
+ logger.info(`Milestones: ${template.milestones}`);
67
+ logger.info(`Expected Issues: ${template.issues}`);
68
+ logger.info(`Best For: ${template.bestFor}`);
69
+ logger.newline();
70
+ }
71
+ }