ai-ship-cli 0.1.0-beta.1

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 (68) hide show
  1. package/.prettierignore +3 -0
  2. package/.prettierrc +7 -0
  3. package/LICENSE +21 -0
  4. package/README.md +156 -0
  5. package/dist/ai/gemini.js +34 -0
  6. package/dist/ai/ollama.js +36 -0
  7. package/dist/analyzers/analyzer.js +93 -0
  8. package/dist/analyzers/compressBranchSummary.js +12 -0
  9. package/dist/analyzers/configAnalyzer.js +18 -0
  10. package/dist/analyzers/detectSignals.js +18 -0
  11. package/dist/analyzers/markupAnalyzer.js +26 -0
  12. package/dist/commands/commit/customAdd.js +1 -0
  13. package/dist/commands/commit/startCommit.js +155 -0
  14. package/dist/commands/commit.js +21 -0
  15. package/dist/commands/config/deleteKey.js +15 -0
  16. package/dist/commands/config.js +65 -0
  17. package/dist/commands/git/startCheckout.js +62 -0
  18. package/dist/commands/git/startCommit.js +91 -0
  19. package/dist/commands/git/startPR.js +51 -0
  20. package/dist/commands/git/startPush.js +24 -0
  21. package/dist/commands/git/startWorkflow.js +71 -0
  22. package/dist/commands/github/github.js +63 -0
  23. package/dist/commands/pr.js +1 -0
  24. package/dist/index.js +38 -0
  25. package/dist/utils/ai.js +22 -0
  26. package/dist/utils/asyncExecuter.js +35 -0
  27. package/dist/utils/files.js +28 -0
  28. package/dist/utils/git.js +106 -0
  29. package/dist/utils/github.js +13 -0
  30. package/dist/utils/helper.js +130 -0
  31. package/dist/utils/inputs.js +20 -0
  32. package/dist/utils/inquirer.js +79 -0
  33. package/dist/utils/parser.js +54 -0
  34. package/dist/utils/print.js +25 -0
  35. package/dist/utils/prompts.js +206 -0
  36. package/dist/utils/runCommit.js +17 -0
  37. package/dist/utils/runConfig.js +35 -0
  38. package/docs/commands.md +106 -0
  39. package/package.json +44 -0
  40. package/src/ai/gemini.ts +27 -0
  41. package/src/ai/ollama.ts +38 -0
  42. package/src/analyzers/analyzer.ts +117 -0
  43. package/src/analyzers/compressBranchSummary.ts +16 -0
  44. package/src/analyzers/configAnalyzer.ts +17 -0
  45. package/src/analyzers/detectSignals.ts +13 -0
  46. package/src/analyzers/markupAnalyzer.ts +25 -0
  47. package/src/commands/commit.ts +18 -0
  48. package/src/commands/config.ts +73 -0
  49. package/src/commands/git/startCheckout.ts +97 -0
  50. package/src/commands/git/startCommit.ts +108 -0
  51. package/src/commands/git/startPR.ts +66 -0
  52. package/src/commands/git/startPush.ts +18 -0
  53. package/src/commands/git/startWorkflow.ts +71 -0
  54. package/src/commands/github/github.ts +72 -0
  55. package/src/commands/pr.ts +0 -0
  56. package/src/index.ts +40 -0
  57. package/src/utils/ai.ts +30 -0
  58. package/src/utils/asyncExecuter.ts +39 -0
  59. package/src/utils/files.ts +30 -0
  60. package/src/utils/git.ts +108 -0
  61. package/src/utils/github.ts +19 -0
  62. package/src/utils/helper.ts +145 -0
  63. package/src/utils/inputs.ts +15 -0
  64. package/src/utils/inquirer.ts +99 -0
  65. package/src/utils/parser.ts +58 -0
  66. package/src/utils/print.ts +16 -0
  67. package/src/utils/prompts.ts +234 -0
  68. package/tsconfig.json +11 -0
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ .env
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 100,
6
+ "tabWidth": 2
7
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Diganta Kr Banik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ <div align="center">
2
+ <img alt="AI-Ship Logo" src="https://img.icons8.com/nolan/256/spaceship.png" width="120" />
3
+ <h1>🚀 AI-SHIP</h1>
4
+ <p><strong>Your ultimate AI-powered terminal assistant for seamless Git workflows.</strong></p>
5
+ <p>Dynamically analyzes your code diffs to automatically write robust and contextual commit messages, generate meaningful branch names, push your changes, and spin up intelligent GitHub Pull Requests—all without ever leaving your terminal.</p>
6
+ </div>
7
+
8
+ <br />
9
+
10
+ ## ✨ Features
11
+
12
+ - **🧠 Deep Code Analysis:** AI-Ship doesn't just read filenames. It reads your raw `git diff` outputs, intelligently filters out noise (like lockfiles), and evaluates the actual intent of your code changes.
13
+ - **📝 Automated Intelligent Commits:** Say goodbye to "fixed bug" or "updated code". AI-Ship drafts rich, standard-compliant commit messages highlighting semantic changes.
14
+ - **🌿 Smart Branch Name Generation:** Need a new feature branch? Throw in `--new-branch` to let AI-Ship read your commit's context, draft a descriptive semantic branch name (e.g. `feature/implement-user-auth`), automatically apply branch collision defenses, and check it out for you.
15
+ - **🚀 One-command PR Pipelines:** Complete your entire development phase in one go. Using `ai-ship commit --push --pr` takes your staged changes, commits them intelligibly, pushes them directly to `origin`, and opens a full contextual Pull Request over the GitHub CLI.
16
+ - **🤖 Provider Agnostic:** Works beautifully with **Google Gemini** (Cloud) out-of-the-box or **Ollama** for entirely private, local execution.
17
+ - **🛡️ Dry Runs & Pre-flights:** Want to see what the AI cooks up without mutating your local Git repository? Use `--dry-run`.
18
+ - **✍️ Interactive Refinements:** Don't like the drafted message? Instantly edit it, request a retry, or skip.
19
+
20
+ ---
21
+
22
+ ## 📦 Prerequisites
23
+
24
+ Before deploying AI-Ship, ensure your machine meets the following structural requirements:
25
+ 1. **[Node.js](https://nodejs.org/)** (v18 or higher recommended)
26
+ 2. **[Git](https://git-scm.com/)** installed and initialized in your working repository.
27
+ 3. **[GitHub CLI (`gh`)](https://cli.github.com/)** installed and authenticated (required exclusively if you use `--pr` flows).
28
+ 4. **An API Access key** for Google Gemini, **OR** a local instance of [Ollama](https://ollama.com/) running locally.
29
+
30
+ ---
31
+
32
+ ## 🛠️ Installation & Setup
33
+
34
+ For now, AI-Ship can be run directly from source.
35
+
36
+ 1. **Clone the repository:**
37
+ ```bash
38
+ git clone https://github.com/developer-diganta/ai-ship.git
39
+ cd ai-ship
40
+ ```
41
+
42
+ 2. **Install dependencies:**
43
+ ```bash
44
+ npm install
45
+ ```
46
+
47
+ 3. **Build the CLI executable:**
48
+ ```bash
49
+ npm run build
50
+ ```
51
+
52
+ 4. **Link globally (Optional but recommended):**
53
+ ```bash
54
+ npm link
55
+ ```
56
+ *This globally registers the `ai-ship` command in your terminal so it can be initiated in any Git repository on your system.*
57
+
58
+ ---
59
+
60
+ ## ⚙️ Configuration
61
+
62
+ Control how AI-Ship operates directly from the CLI via the `config` command.
63
+
64
+ ### 🔑 Set up your API Key (Cloud/Gemini)
65
+ To utilize the Gemini API connection:
66
+ ```bash
67
+ ai-ship config --add-key
68
+ ```
69
+ *It will securely prompt for your API key and store it locally.*
70
+
71
+ To remove your key:
72
+ ```bash
73
+ ai-ship config --delete-key
74
+ ```
75
+
76
+ ### 🧠 Change AI Provider (Local vs Cloud)
77
+ If you prefer running a local execution without making network API calls to Cloud AI models, you can switch providers. *(Requires a local Ollama server)*:
78
+ ```bash
79
+ ai-ship config set provider local
80
+ ```
81
+ To switch back to the cloud via Google Gemini:
82
+ ```bash
83
+ ai-ship config set provider cloud
84
+ ```
85
+
86
+ ### 🧾 View Current Config
87
+ ```bash
88
+ ai-ship config show --verbose
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 🚢 Usage
94
+
95
+ At its core, `ai-ship` hooks into your uncommitted, staged files.
96
+
97
+ ### Basic Usage
98
+
99
+ Stage your files as usual (`git add .`), then draft an AI commit:
100
+ ```bash
101
+ ai-ship commit
102
+ ```
103
+ > *Note: If you run `ai-ship commit` without staging, AI-Ship will automatically run `git add -A` for you!*
104
+
105
+ You can also pass specific files to be explicitly staged:
106
+ ```bash
107
+ ai-ship commit src/index.ts src/utils/helper.ts
108
+ ```
109
+
110
+ ### Advanced Pipeline Workflows
111
+
112
+ AI-Ship can condense the 5 basic Git commands down into **one single instruction**.
113
+
114
+ **🔥 The Ultimate CLI Combo (Commit, Branch, Push, PR):**
115
+ ```bash
116
+ ai-ship commit --new-branch --push --pr --yes
117
+ ```
118
+ *What this single command achieved:*
119
+ 1. Auto-staged all tracked changes.
120
+ 2. Read the unified diff and generated a beautiful Commit Message.
121
+ 3. Examined that context and automatically made a new branch (e.g., `feature/add-payment-gate`).
122
+ 4. Directly pushed the new upstream branch to origin.
123
+ 5. Invoked the `gh` CLI to summarize the commit history into a Pull Request Description.
124
+ 6. Auto-published the PR on GitHub without asking for manual confirmation (`--yes`).
125
+
126
+ ### 🎌 Command Flags
127
+ Append these arguments upon the `commit` hook to modify AI-Ship's behavior:
128
+
129
+ | Flag | Action |
130
+ | --- | --- |
131
+ | `--new-branch` | Analyzes commit context, drafts a semantic branch name, & securely checks it out. |
132
+ | `--push` | Triggers a `git push`. If there isn't an upstream anchor configured, securely configures tracking mappings for you. |
133
+ | `--pr` | Utilizes the GitHub CLI to publish an intelligent PR onto default target branches. Prompts interactions manually if omitted. |
134
+ | `--target-branch <branch>` | Manually overrides the base branch point targeting your `--pr`. |
135
+ | `--yes` | Headless execution. Skips interactive "Edit/Continue/Cancel" refinements and accepts the AI's first guess automatically. |
136
+ | `--dry-run` | Reads files, interfaces with the AI layer, and outputs responses without actually performing Git mutations. Great for observing syntax. |
137
+ | `--model <provider>` | Inline injection overriding the globally chosen logic model (e.g `--model local` or `--model cloud`). |
138
+
139
+ ---
140
+
141
+ ## 🤝 Contributing
142
+
143
+ Contributions are heavily welcomed!
144
+ 1. **Fork** the repository
145
+ 2. Implement your magic fix or feature branch (`git checkout -b feature/magic-fix`)
146
+ 3. Pass through Prettier & Typescript integrations by running `npm run build && npm run format`
147
+ 4. Use AI-Ship to push your PR logic gracefully.
148
+ 5. Submit your **Pull Request**.
149
+
150
+ If you locate any discrepancies or issues, please [file a new GitHub Issue](https://github.com/developer-diganta/ai-ship/issues).
151
+
152
+ <br />
153
+
154
+ <div align="center">
155
+ <p>Built with 🩵 by <a href="https://github.com/developer-diganta">Diganta</a></p>
156
+ </div>
@@ -0,0 +1,34 @@
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.generateWithGemini = void 0;
7
+ const genai_1 = require("@google/genai");
8
+ const helper_1 = require("../utils/helper");
9
+ const inputs_1 = require("../utils/inputs");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
12
+ let apiKey = (0, helper_1.getCurrentConfig)('geminiApiKey') || GEMINI_API_KEY;
13
+ let ai = new genai_1.GoogleGenAI({ apiKey });
14
+ const generateWithGemini = async (prompt) => {
15
+ try {
16
+ if (!apiKey) {
17
+ console.log('Gemini API key not found.');
18
+ apiKey = await (0, inputs_1.askApiKey)();
19
+ (0, helper_1.saveValueToConfig)('geminiApiKey', apiKey);
20
+ ai = new genai_1.GoogleGenAI({ apiKey });
21
+ console.log('API key saved!');
22
+ }
23
+ const response = await ai.models.generateContent({
24
+ model: 'gemini-2.5-flash',
25
+ contents: prompt,
26
+ });
27
+ return response.text || '';
28
+ }
29
+ catch (err) {
30
+ console.log(chalk_1.default.red(`We ran into an error: ${chalk_1.default.bold.red(err)}`));
31
+ return '';
32
+ }
33
+ };
34
+ exports.generateWithGemini = generateWithGemini;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateWithGemma = void 0;
4
+ const helper_1 = require("../utils/helper");
5
+ const generateWithGemma = async (prompt) => {
6
+ try {
7
+ const endpoint = (0, helper_1.getCurrentConfig)('localEndpoint') || 'http://127.0.0.1:11434';
8
+ const model = (0, helper_1.getCurrentConfig)('model') || 'gemma:2b';
9
+ const res = await fetch(`${endpoint}/api/generate`, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ },
14
+ body: JSON.stringify({
15
+ model: model,
16
+ prompt,
17
+ stream: false,
18
+ options: {
19
+ temperature: 0.1,
20
+ },
21
+ }),
22
+ });
23
+ const data = await res.json();
24
+ if (!res.ok || data.error) {
25
+ throw new Error(data.error || `HTTP error! status: ${res.status}`);
26
+ }
27
+ if (data.response) {
28
+ return data.response.trim();
29
+ }
30
+ throw new Error('Unexpected response format from local model');
31
+ }
32
+ catch (e) {
33
+ throw new Error(`Failed to connect to local model: ${e.message}`);
34
+ }
35
+ };
36
+ exports.generateWithGemma = generateWithGemma;
@@ -0,0 +1,93 @@
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.analyzeDiff = void 0;
7
+ const detectSignals_1 = require("./detectSignals");
8
+ const markupAnalyzer_1 = __importDefault(require("./markupAnalyzer"));
9
+ const configAnalyzer_1 = __importDefault(require("./configAnalyzer"));
10
+ const CODE_EXT = [
11
+ '.js',
12
+ '.ts',
13
+ '.jsx',
14
+ '.tsx',
15
+ '.py',
16
+ '.java',
17
+ '.go',
18
+ '.rs',
19
+ '.cpp',
20
+ '.c',
21
+ '.h',
22
+ '.hpp',
23
+ '.cs',
24
+ '.php',
25
+ '.rb',
26
+ ];
27
+ const MARKUP_EXT = ['.html', '.css', '.scss', '.less'];
28
+ const CONFIG_EXT = ['.json', '.yaml', '.yml', '.toml'];
29
+ const getFileType = (file) => {
30
+ const lower = file.toLowerCase();
31
+ if (CODE_EXT.some((ext) => lower.endsWith(ext)))
32
+ return 'code';
33
+ if (MARKUP_EXT.some((ext) => lower.endsWith(ext)))
34
+ return 'markup';
35
+ if (CONFIG_EXT.some((ext) => lower.endsWith(ext)) ||
36
+ lower === 'dockerfile' ||
37
+ lower.includes('package.json'))
38
+ return 'config';
39
+ return 'other';
40
+ };
41
+ const analyzeDiff = (diff) => {
42
+ try {
43
+ const files = diff.split('diff --git').filter(Boolean);
44
+ const summaries = [];
45
+ for (const chunk of files) {
46
+ const lines = chunk.split('\n');
47
+ const fileMatch = lines[0]?.match(/a\/(.+?) b\/(.+)/);
48
+ const file = fileMatch?.[2] || 'unknown';
49
+ const fileType = getFileType(file);
50
+ if (fileType === 'markup') {
51
+ summaries.push((0, markupAnalyzer_1.default)(file, lines));
52
+ continue;
53
+ }
54
+ if (fileType === 'config') {
55
+ summaries.push((0, configAnalyzer_1.default)(file, lines));
56
+ continue;
57
+ }
58
+ // default = code analyzer
59
+ let additions = 0;
60
+ let deletions = 0;
61
+ const snippet = [];
62
+ const signals = [];
63
+ for (const line of lines) {
64
+ if (line.startsWith('+') && !line.startsWith('+++')) {
65
+ additions++;
66
+ const code = line.slice(1).trim();
67
+ if (snippet.length < 20)
68
+ snippet.push(`+ ${code}`);
69
+ (0, detectSignals_1.detectSignals)(code, signals);
70
+ }
71
+ if (line.startsWith('-') && !line.startsWith('---')) {
72
+ deletions++;
73
+ const code = line.slice(1).trim();
74
+ if (snippet.length < 20)
75
+ snippet.push(`- ${code}`);
76
+ }
77
+ }
78
+ summaries.push({
79
+ file,
80
+ additions,
81
+ deletions,
82
+ signals: [...new Set(signals)],
83
+ snippet,
84
+ });
85
+ }
86
+ return summaries;
87
+ }
88
+ catch (e) {
89
+ console.log({ e });
90
+ return [];
91
+ }
92
+ };
93
+ exports.analyzeDiff = analyzeDiff;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compressBranchSummary = void 0;
4
+ const compressBranchSummary = (summary) => {
5
+ return summary
6
+ .slice(0, 8) // limit files
7
+ .map((s) => ({
8
+ file: s.file,
9
+ signals: s.signals,
10
+ }));
11
+ };
12
+ exports.compressBranchSummary = compressBranchSummary;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (file, lines) => {
4
+ const snippet = [];
5
+ for (const line of lines) {
6
+ if (line.startsWith('+') || line.startsWith('-')) {
7
+ if (snippet.length < 30)
8
+ snippet.push(line);
9
+ }
10
+ }
11
+ return {
12
+ file,
13
+ additions: snippet.filter((l) => l.startsWith('+')).length,
14
+ deletions: snippet.filter((l) => l.startsWith('-')).length,
15
+ signals: ['config updated'],
16
+ snippet,
17
+ };
18
+ };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectSignals = void 0;
4
+ const detectSignals = (line, signals) => {
5
+ if (/function\s+\w+/.test(line))
6
+ signals.push('function added');
7
+ if (/class\s+\w+/.test(line))
8
+ signals.push('class added');
9
+ if (/import\s+/.test(line))
10
+ signals.push('imports updated');
11
+ if (/export\s+/.test(line))
12
+ signals.push('exports updated');
13
+ if (/FROM\s+/.test(line))
14
+ signals.push('docker base image change');
15
+ if (/version/.test(line))
16
+ signals.push('version updated');
17
+ };
18
+ exports.detectSignals = detectSignals;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (file, lines) => {
4
+ const snippet = [];
5
+ let additions = 0;
6
+ let deletions = 0;
7
+ for (const line of lines) {
8
+ if (line.startsWith('+') && !line.startsWith('+++')) {
9
+ additions++;
10
+ if (snippet.length < 20)
11
+ snippet.push(line);
12
+ }
13
+ if (line.startsWith('-') && !line.startsWith('---')) {
14
+ deletions++;
15
+ if (snippet.length < 20)
16
+ snippet.push(line);
17
+ }
18
+ }
19
+ return {
20
+ file,
21
+ additions,
22
+ deletions,
23
+ signals: ['markup updated'],
24
+ snippet,
25
+ };
26
+ };
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,155 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const helper_1 = require("../../utils/helper");
8
+ const inputs_1 = require("../../utils/inputs");
9
+ const git_1 = require("../../utils/git");
10
+ const files_1 = require("../../utils/files");
11
+ const prompts_1 = require("../../utils/prompts");
12
+ const parser_1 = require("../../utils/parser");
13
+ const genai_1 = require("@google/genai");
14
+ const analyzer_1 = require("../../analyzers/analyzer");
15
+ const compressBranchSummary_1 = require("../../analyzers/compressBranchSummary");
16
+ const ollama_1 = require("../../ai/ollama");
17
+ const inquirer_1 = __importDefault(require("inquirer"));
18
+ // @ts-ignore
19
+ const enquirer_1 = require("enquirer");
20
+ const ora_1 = __importDefault(require("ora"));
21
+ const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
22
+ let apiKey = (0, helper_1.loadApiKey)();
23
+ exports.default = async () => {
24
+ try {
25
+ if (!apiKey) {
26
+ console.log('Gemini API key not found.');
27
+ apiKey = await (0, inputs_1.askApiKey)();
28
+ (0, helper_1.saveApiKey)(apiKey);
29
+ console.log('API key saved!');
30
+ }
31
+ let ai = new genai_1.GoogleGenAI({ apiKey });
32
+ console.log('');
33
+ console.log(chalk_1.default.bold.bgBlue(' 🚀 AI-SHIP ') + chalk_1.default.bold.blue(' Commit Generator '));
34
+ console.log(chalk_1.default.dim('==================================='));
35
+ console.log('');
36
+ // 1️⃣ Get staged files
37
+ const scanSpinner = (0, ora_1.default)('Scanning staged files...').start();
38
+ const filesChanged = await (0, git_1.getFilesChanged)();
39
+ if (!filesChanged.length) {
40
+ scanSpinner.fail(chalk_1.default.yellow('No staged files detected.'));
41
+ return;
42
+ }
43
+ // 2️⃣ Expand directories
44
+ const expandedFiles = (0, files_1.expandDirectories)(filesChanged);
45
+ // 3️⃣ Extract filenames
46
+ let filenames = expandedFiles.map((f) => f.file);
47
+ // 4️⃣ Filter noise files
48
+ filenames = (0, parser_1.filterNoiseFiles)(filenames);
49
+ scanSpinner.succeed(`Found ${filesChanged.length} staged file(s).`);
50
+ console.log(chalk_1.default.dim('Files changed:'));
51
+ filesChanged.forEach((f) => console.log(chalk_1.default.green(` + ${f.file}`)));
52
+ console.log('');
53
+ // 5️⃣ Analyze diff
54
+ const analyzeSpinner = (0, ora_1.default)('Analyzing changes and checking branches...').start();
55
+ const diffs = await (0, git_1.getStagedDiff)(filenames);
56
+ const diffSummary = (0, analyzer_1.analyzeDiff)(diffs);
57
+ await (0, git_1.gitFetch)();
58
+ const allBranches = await (0, git_1.getAllBranches)();
59
+ const branchSummary = (0, compressBranchSummary_1.compressBranchSummary)(diffSummary);
60
+ const currentBranch = await (0, git_1.getCurrentBranchName)();
61
+ analyzeSpinner.succeed('Analysis complete.\n');
62
+ // 6️⃣ Commit Message Loop
63
+ let commitMessage = '';
64
+ let commitAccepted = false;
65
+ while (!commitAccepted) {
66
+ const commitSpinner = (0, ora_1.default)('Generating commit message...').start();
67
+ const prompt = (0, prompts_1.buildCommitPromptGemma)(diffSummary);
68
+ // const prompt = buildCommitPrompt(diffSummary);
69
+ commitMessage = await (0, ollama_1.generateWithGemma)(prompt);
70
+ // const response = await ai.models.generateContent({
71
+ // model: 'gemini-2.5-flash',
72
+ // contents: prompt,
73
+ // });
74
+ // console.log(response.text);
75
+ // commitMessage = (responseCommit.text || '').trim();
76
+ // commitMessage = response.text || '';
77
+ commitSpinner.succeed('Commit message generated:\n');
78
+ console.log(chalk_1.default.cyan(commitMessage));
79
+ console.log('');
80
+ const { action } = await inquirer_1.default.prompt([
81
+ {
82
+ type: 'list',
83
+ name: 'action',
84
+ message: 'What would you like to do with this commit message?',
85
+ choices: ['Continue', 'Edit', 'Retry'],
86
+ },
87
+ ]);
88
+ if (action === 'Continue') {
89
+ commitAccepted = true;
90
+ }
91
+ else if (action === 'Edit') {
92
+ const promptInput = new enquirer_1.Input({
93
+ message: 'Edit your commit message:',
94
+ initial: commitMessage,
95
+ });
96
+ commitMessage = (await promptInput.run());
97
+ commitAccepted = true;
98
+ }
99
+ else if (action === 'Retry') {
100
+ console.log(chalk_1.default.yellow('Retrying commit message...'));
101
+ console.log('');
102
+ }
103
+ }
104
+ // 7️⃣ Commit
105
+ const commitProcessSpinner = (0, ora_1.default)('Committing changes...').start();
106
+ await (0, git_1.gitCommit)(commitMessage);
107
+ commitProcessSpinner.succeed('Changes successfully committed!\n');
108
+ // 8️⃣ Branch Name Loop
109
+ let branchName = '';
110
+ let branchAccepted = false;
111
+ while (!branchAccepted) {
112
+ const branchSpinner = (0, ora_1.default)('Generating branch name...').start();
113
+ const branchNamePrompt = (0, prompts_1.buildBranchPromptGemma)(branchSummary, allBranches, currentBranch, commitMessage);
114
+ const responseBranch = await (0, ollama_1.generateWithGemma)(branchNamePrompt);
115
+ branchName = (responseBranch.text || '').trim().replace(/['"]/g, '');
116
+ branchSpinner.succeed('Branch name generated:\n');
117
+ console.log(chalk_1.default.magenta(branchName));
118
+ console.log('');
119
+ const { action } = await inquirer_1.default.prompt([
120
+ {
121
+ type: 'list',
122
+ name: 'action',
123
+ message: 'What would you like to do with this branch name?',
124
+ choices: ['Continue', 'Edit', 'Retry'],
125
+ },
126
+ ]);
127
+ if (action === 'Continue') {
128
+ branchAccepted = true;
129
+ }
130
+ else if (action === 'Edit') {
131
+ const promptInput = new enquirer_1.Input({
132
+ message: 'Edit your branch name:',
133
+ initial: branchName,
134
+ });
135
+ branchName = (await promptInput.run());
136
+ branchAccepted = true;
137
+ }
138
+ else if (action === 'Retry') {
139
+ console.log(chalk_1.default.yellow('Retrying branch name...'));
140
+ console.log('');
141
+ }
142
+ }
143
+ // 9️⃣ Rename Branch
144
+ const branchProcessSpinner = (0, ora_1.default)('Applying branch name...').start();
145
+ await (0, git_1.gitRenameBranch)(branchName);
146
+ branchProcessSpinner.succeed(`Branch correctly renamed to ${chalk_1.default.bold(branchName)}!`);
147
+ }
148
+ catch (err) {
149
+ if (err.name === 'ExitPromptError') {
150
+ console.log(chalk_1.default.yellow('\nProcess aborted using user prompt.\n'));
151
+ return;
152
+ }
153
+ (0, helper_1.log)(chalk_1.default.red(`We ran into an error: ${err}`));
154
+ }
155
+ };
@@ -0,0 +1,21 @@
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
+ const ora_1 = __importDefault(require("ora"));
7
+ const startWorkflow_1 = __importDefault(require("./git/startWorkflow"));
8
+ const git_1 = require("../utils/git");
9
+ exports.default = async (payload = [], flags = {}) => {
10
+ const stageSpinner = (0, ora_1.default)('Staging files...').start();
11
+ if (payload.length > 0) {
12
+ await (0, git_1.stageFiles)(payload);
13
+ stageSpinner.succeed(`Staged ${payload.length} specified file(s).`);
14
+ }
15
+ else {
16
+ await (0, git_1.stageAll)();
17
+ stageSpinner.succeed('Staged all changes.');
18
+ }
19
+ console.log('');
20
+ await (0, startWorkflow_1.default)(flags);
21
+ };
@@ -0,0 +1,15 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const helper_1 = require("../../utils/helper");
8
+ exports.default = () => {
9
+ if ((0, helper_1.deleteApiKey)()) {
10
+ (0, helper_1.log)(chalk_1.default.green('API Key Deleted'));
11
+ }
12
+ else {
13
+ (0, helper_1.log)(chalk_1.default.red('API Key Could Not Be Deleted. API KEY NOT FOUND!'));
14
+ }
15
+ };