git-devflow 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,160 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GitHubService = void 0;
7
+ const rest_1 = require("@octokit/rest");
8
+ const child_process_1 = require("child_process");
9
+ const util_1 = require("util");
10
+ const config_1 = require("./config");
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
13
+ class GitHubService {
14
+ constructor() {
15
+ this.owner = '';
16
+ this.repo = '';
17
+ // Get token from environment
18
+ // Try multiple sources for token
19
+ const configService = new config_1.ConfigService();
20
+ const token = configService.getToken();
21
+ if (!token) {
22
+ throw new Error('GitHub token not found. Either:\n 1. Set GITHUB_TOKEN in .env\n 2. Run: gh auth login');
23
+ }
24
+ this.octokit = new rest_1.Octokit({ auth: token });
25
+ try {
26
+ const { owner, repo } = this.parseRemoteUrl();
27
+ console.log(chalk_1.default.dim(`✓ Connected to ${owner}/${repo}`));
28
+ }
29
+ catch (parseError) {
30
+ throw new Error('Not a GitHub repository or no remote configured');
31
+ }
32
+ }
33
+ async init() {
34
+ try {
35
+ const { stdout } = await execAsync('git remote get-url origin');
36
+ const url = stdout.trim();
37
+ let match;
38
+ // Try SSH format with custom host: git@github-personal:user/repo.git
39
+ match = url.match(/git@([^:]+):(.+?)\/(.+?)(\.git)?$/);
40
+ if (match) {
41
+ this.owner = match[2];
42
+ this.repo = match[3].replace('.git', '');
43
+ console.log(chalk_1.default.green('✓ Parsed:'), `${this.owner}/${this.repo}`);
44
+ return;
45
+ }
46
+ // Try SSH format: git@github.com:user/repo.git
47
+ match = url.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
48
+ if (!match) {
49
+ // Try HTTPS format: https://github.com/user/repo.git
50
+ match = url.match(/github\.com[:/](.+?)\/(.+?)(\.git)?$/);
51
+ }
52
+ if (match) {
53
+ this.owner = match[1];
54
+ this.repo = match[2].replace('.git', '');
55
+ }
56
+ else {
57
+ throw new Error('Could not parse GitHub repository from remote URL. Supported formats: SSH or HTTPS.');
58
+ }
59
+ }
60
+ catch (error) {
61
+ if (error instanceof Error && error.message.startsWith('Could not parse')) {
62
+ throw error;
63
+ }
64
+ throw new Error('Not a GitHub repository or no remote configured');
65
+ }
66
+ }
67
+ // Get issue details
68
+ async getIssue(issueNumber) {
69
+ try {
70
+ const { owner, repo } = this.parseRemoteUrl();
71
+ const { data } = await this.octokit.rest.issues.get({
72
+ owner,
73
+ repo,
74
+ issue_number: issueNumber
75
+ });
76
+ return {
77
+ number: data.number,
78
+ title: data.title ?? null,
79
+ body: data.body ?? null,
80
+ state: data.state,
81
+ labels: data.labels.map((label) => typeof label === 'string' ? label : label.name),
82
+ html_url: data.html_url
83
+ };
84
+ }
85
+ catch (error) {
86
+ if (error.status === 404) {
87
+ throw new Error(`Issue #${issueNumber} not found`);
88
+ }
89
+ if (error.status === 401 || error.status === 403) {
90
+ throw new Error('GitHub authentication failed. Check your token with: devflow config setup');
91
+ }
92
+ throw new Error(`Failed to fetch issue #${issueNumber} (HTTP ${error.status || 'unknown'})`);
93
+ }
94
+ }
95
+ // Create a pull request
96
+ async createPullRequest(params) {
97
+ await this.init();
98
+ try {
99
+ const { data } = await this.octokit.pulls.create({
100
+ owner: this.owner,
101
+ repo: this.repo,
102
+ title: params.title,
103
+ body: params.body,
104
+ head: params.head,
105
+ base: params.base
106
+ });
107
+ return data;
108
+ }
109
+ catch (error) {
110
+ if (error.status === 401 || error.status === 403) {
111
+ throw new Error('GitHub authentication failed. Check your token with: devflow config setup');
112
+ }
113
+ if (error.status === 422) {
114
+ throw new Error('Failed to create pull request. A PR may already exist for this branch, or the branch has no changes.');
115
+ }
116
+ throw new Error(`Failed to create pull request (HTTP ${error.status || 'unknown'})`);
117
+ }
118
+ }
119
+ // Get repo info
120
+ getRepoInfo() {
121
+ return { owner: this.owner, repo: this.repo };
122
+ }
123
+ parseRemoteUrl() {
124
+ try {
125
+ const { execSync } = require('child_process');
126
+ const remoteUrl = execSync('git config --get remote.origin.url', {
127
+ encoding: 'utf-8'
128
+ }).trim();
129
+ // Handle SSH format with custom host: git@github-personal:owner/repo.git
130
+ const sshCustomMatch = remoteUrl.match(/git@([^:]+):(.+?)\/(.+?)(\.git)?$/);
131
+ if (sshCustomMatch) {
132
+ return {
133
+ owner: sshCustomMatch[2],
134
+ repo: sshCustomMatch[3]
135
+ };
136
+ }
137
+ // Handle SSH format: git@github.com:owner/repo.git
138
+ const sshMatch = remoteUrl.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
139
+ if (sshMatch) {
140
+ return {
141
+ owner: sshMatch[1],
142
+ repo: sshMatch[2]
143
+ };
144
+ }
145
+ // Handle HTTPS format: https://github.com/owner/repo.git
146
+ const httpsMatch = remoteUrl.match(/github\.com\/(.+?)\/(.+?)(\.git)?$/);
147
+ if (httpsMatch) {
148
+ return {
149
+ owner: httpsMatch[1],
150
+ repo: httpsMatch[2]
151
+ };
152
+ }
153
+ throw new Error('Could not parse GitHub remote URL. Supported formats: SSH (git@github.com:owner/repo) or HTTPS (https://github.com/owner/repo)');
154
+ }
155
+ catch (error) {
156
+ throw new Error('Could not find GitHub remote. Make sure you have a remote named "origin"');
157
+ }
158
+ }
159
+ }
160
+ exports.GitHubService = GitHubService;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Spinner = void 0;
7
+ const ora_1 = __importDefault(require("ora"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ class Spinner {
10
+ constructor(text) {
11
+ this.spinner = (0, ora_1.default)({
12
+ text: chalk_1.default.cyan(text),
13
+ spinner: 'dots'
14
+ });
15
+ }
16
+ start() {
17
+ this.spinner.start();
18
+ }
19
+ succeed(text) {
20
+ if (text) {
21
+ this.spinner.succeed(chalk_1.default.green(text));
22
+ }
23
+ else {
24
+ this.spinner.succeed();
25
+ }
26
+ }
27
+ fail(text) {
28
+ if (text) {
29
+ this.spinner.fail(chalk_1.default.red(text));
30
+ }
31
+ else {
32
+ this.spinner.fail();
33
+ }
34
+ }
35
+ update(text) {
36
+ this.spinner.text = chalk_1.default.cyan(text);
37
+ }
38
+ stop() {
39
+ this.spinner.stop();
40
+ }
41
+ }
42
+ exports.Spinner = Spinner;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "git-devflow",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered Git workflow automation using GitHub Copilot CLI",
5
+ "main": "dist/index.js",
6
+ "files": [
7
+ "dist",
8
+ "README.md"
9
+ ],
10
+ "scripts": {
11
+ "dev": "tsx src/index.ts",
12
+ "build": "tsc",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "git",
18
+ "copilot",
19
+ "ai",
20
+ "commit",
21
+ "pull-request",
22
+ "branch",
23
+ "workflow",
24
+ "automation",
25
+ "github",
26
+ "cli"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/karthikeyan-shanmugam-at/devflow"
33
+ },
34
+ "dependencies": {
35
+ "@inquirer/prompts": "^8.2.0",
36
+ "@octokit/rest": "^22.0.1",
37
+ "chalk": "^5.6.2",
38
+ "commander": "^14.0.2",
39
+ "ora": "^9.3.0",
40
+ "simple-git": "^3.30.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^25.0.10",
44
+ "tsx": "^4.21.0",
45
+ "typescript": "^5.9.3"
46
+ },
47
+ "bin": {
48
+ "devflow": "./dist/index.js"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }