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,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGithubPR = void 0;
4
+ const createGithubPR = async ({ title, body, base = 'main', head, }) => {
5
+ try {
6
+ await (0, exports.createGithubPR)({ title, body, base, head });
7
+ }
8
+ catch (error) {
9
+ console.error('Failed to create GitHub PR.');
10
+ throw error;
11
+ }
12
+ };
13
+ exports.createGithubPR = createGithubPR;
@@ -0,0 +1,130 @@
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.getProvider = exports.saveValueToConfig = exports.jsonConfig = exports.verboseConfig = exports.getCurrentConfig = exports.deleteConfigKey = exports.CONFIG_FILE = exports.CONFIG_DIR = exports.log = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const ALLOWED_KEYS = ['provider', 'model', 'localEndpoint', 'geminiApiKey'];
12
+ const log = (data) => console.log(data);
13
+ exports.log = log;
14
+ // config dir for storing api key
15
+ exports.CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.ai-ship');
16
+ // config file for storing api key
17
+ exports.CONFIG_FILE = path_1.default.join(exports.CONFIG_DIR, 'config.json');
18
+ const deleteConfigKey = (key) => {
19
+ if (!fs_1.default.existsSync(exports.CONFIG_FILE))
20
+ return false;
21
+ try {
22
+ const raw = fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
23
+ const config = raw ? JSON.parse(raw) : {};
24
+ if (config[key]) {
25
+ delete config[key];
26
+ fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ };
35
+ exports.deleteConfigKey = deleteConfigKey;
36
+ const getCurrentConfig = (key = 'all') => {
37
+ if (!fs_1.default.existsSync(exports.CONFIG_FILE))
38
+ return null;
39
+ const config = JSON.parse(fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8'));
40
+ if (key === 'all')
41
+ return config;
42
+ return config[key];
43
+ };
44
+ exports.getCurrentConfig = getCurrentConfig;
45
+ const verboseConfig = (config) => {
46
+ const { provider, model, localEndpoint, geminiApiKey } = config;
47
+ const maskedKey = geminiApiKey
48
+ ? geminiApiKey.slice(0, 4) + '...' + geminiApiKey.slice(-4)
49
+ : 'not configured';
50
+ console.log(chalk_1.default.bold('\nAI-Ship Configuration\n'));
51
+ console.log(chalk_1.default.yellow('Provider'));
52
+ console.log(' Current:', provider || 'not set');
53
+ console.log(' Description: Determines where AI runs (local via Ollama or cloud API)\n');
54
+ console.log(chalk_1.default.yellow('Model'));
55
+ console.log(' Current:', model || 'not set');
56
+ console.log(' Description: AI model used for commit and branch generation\n');
57
+ console.log(chalk_1.default.yellow('Local Model Settings'));
58
+ console.log(' Endpoint:', localEndpoint || 'not configured');
59
+ console.log(' Description: URL of the local model server (e.g. http://127.0.0.1:11434)\n');
60
+ console.log(chalk_1.default.yellow('Cloud Model Settings'));
61
+ console.log(' Gemini API Key:', maskedKey);
62
+ console.log(' Description: API key used when provider is set to cloud\n');
63
+ };
64
+ exports.verboseConfig = verboseConfig;
65
+ const jsonConfig = (config) => {
66
+ const safeConfig = {
67
+ provider: config.provider || null,
68
+ model: config.model || null,
69
+ localEndpoint: config.localEndpoint || null,
70
+ geminiApiKey: config.geminiApiKey
71
+ ? config.geminiApiKey.slice(0, 4) + '...' + config.geminiApiKey.slice(-4)
72
+ : null,
73
+ };
74
+ console.log(JSON.stringify(safeConfig, null, 2));
75
+ };
76
+ exports.jsonConfig = jsonConfig;
77
+ const validateValue = (key, value) => {
78
+ switch (key) {
79
+ case 'provider':
80
+ if (!['local', 'cloud'].includes(value)) {
81
+ throw new Error("provider must be 'local' or 'cloud'");
82
+ }
83
+ break;
84
+ case 'model':
85
+ if (typeof value !== 'string' || value.trim().length === 0) {
86
+ throw new Error('model must be a non-empty string');
87
+ }
88
+ break;
89
+ case 'localEndpoint':
90
+ try {
91
+ new URL(value);
92
+ }
93
+ catch {
94
+ throw new Error('localEndpoint must be a valid URL');
95
+ }
96
+ break;
97
+ case 'geminiApiKey':
98
+ if (typeof value !== 'string' || value.trim().length === 0) {
99
+ throw new Error('geminiApiKey must be a valid string');
100
+ }
101
+ break;
102
+ }
103
+ };
104
+ const saveValueToConfig = (key, value) => {
105
+ try {
106
+ if (!ALLOWED_KEYS.includes(key)) {
107
+ throw new Error(`Invalid config key: ${key}. Allowed keys: ${ALLOWED_KEYS.join(', ')}`);
108
+ }
109
+ validateValue(key, value);
110
+ if (!fs_1.default.existsSync(exports.CONFIG_DIR)) {
111
+ fs_1.default.mkdirSync(exports.CONFIG_DIR, { recursive: true });
112
+ }
113
+ let config = {};
114
+ if (fs_1.default.existsSync(exports.CONFIG_FILE)) {
115
+ const raw = fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
116
+ config = raw ? JSON.parse(raw) : {};
117
+ }
118
+ config[key] = value;
119
+ fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
120
+ console.log(chalk_1.default.green(`✔ Config updated: ${chalk_1.default.bold.green(key)}`));
121
+ }
122
+ catch (err) {
123
+ console.error(chalk_1.default.red(`❌ Failed to save config: ${chalk_1.default.bold.red(err.message)}`));
124
+ }
125
+ };
126
+ exports.saveValueToConfig = saveValueToConfig;
127
+ const getProvider = () => {
128
+ return (0, exports.getCurrentConfig)('provider');
129
+ };
130
+ exports.getProvider = getProvider;
@@ -0,0 +1,20 @@
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.askApiKey = void 0;
7
+ const readline_1 = __importDefault(require("readline"));
8
+ const askApiKey = () => {
9
+ const rl = readline_1.default.createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout,
12
+ });
13
+ return new Promise((resolve) => {
14
+ rl.question('Enter your Gemini API key: ', (answer) => {
15
+ rl.close();
16
+ resolve(answer.trim());
17
+ });
18
+ });
19
+ };
20
+ exports.askApiKey = askApiKey;
@@ -0,0 +1,79 @@
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.interactivePRPrompt = exports.interactivePushPrompt = exports.interactiveRefinePrompt = void 0;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ // @ts-ignore
9
+ const enquirer_1 = require("enquirer");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const interactiveRefinePrompt = async (itemType, initialValue) => {
12
+ const { action } = await inquirer_1.default.prompt([
13
+ {
14
+ type: 'list',
15
+ name: 'action',
16
+ message: `What would you like to do with this ${itemType}?`,
17
+ choices: ['Continue', 'Edit', 'Retry', 'Cancel'],
18
+ },
19
+ ]);
20
+ if (action === 'Continue') {
21
+ return { accepted: true, value: initialValue, cancel: false };
22
+ }
23
+ if (action === 'Edit') {
24
+ const promptInput = new enquirer_1.Input({
25
+ message: `Edit your ${itemType}:`,
26
+ initial: initialValue,
27
+ });
28
+ const editedValue = (await promptInput.run());
29
+ return { accepted: true, value: editedValue, cancel: false };
30
+ }
31
+ if (action === 'Retry') {
32
+ console.log(chalk_1.default.yellow(`Retrying ${itemType}...\\n`));
33
+ return { accepted: false, value: initialValue, cancel: false };
34
+ }
35
+ // Cancel
36
+ console.log(chalk_1.default.yellow(`${itemType.charAt(0).toUpperCase() + itemType.slice(1)} cancelled.\\n`));
37
+ return { accepted: false, value: initialValue, cancel: true };
38
+ };
39
+ exports.interactiveRefinePrompt = interactiveRefinePrompt;
40
+ const interactivePushPrompt = async () => {
41
+ const { action } = await inquirer_1.default.prompt([
42
+ {
43
+ type: 'list',
44
+ name: 'action',
45
+ message: `Do you want to push your committed changes to the remote repository?`,
46
+ choices: ['Yes', 'No'],
47
+ },
48
+ ]);
49
+ if (action === 'Yes') {
50
+ return { accepted: true, cancel: false };
51
+ }
52
+ console.log(chalk_1.default.yellow(`Push cancelled\n`));
53
+ return { accepted: false, cancel: true };
54
+ };
55
+ exports.interactivePushPrompt = interactivePushPrompt;
56
+ const interactivePRPrompt = async (defaultBase = 'main') => {
57
+ const { action } = await inquirer_1.default.prompt([
58
+ {
59
+ type: 'list',
60
+ name: 'action',
61
+ message: `Do you want to create a pull request?`,
62
+ choices: ['Yes', 'No'],
63
+ },
64
+ ]);
65
+ if (action === 'Yes') {
66
+ const { base } = await inquirer_1.default.prompt([
67
+ {
68
+ type: 'input',
69
+ name: 'base',
70
+ message: `Which branch do you want to target?`,
71
+ default: defaultBase,
72
+ },
73
+ ]);
74
+ return { accepted: true, cancel: false, base };
75
+ }
76
+ console.log(chalk_1.default.yellow(`PR creation cancelled\n`));
77
+ return { accepted: false, cancel: true, base: defaultBase };
78
+ };
79
+ exports.interactivePRPrompt = interactivePRPrompt;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterNoiseFiles = exports.isNoiseFile = exports.NOISE_PATTERNS = void 0;
4
+ const LOCK_FILES = [
5
+ '*.lock',
6
+ 'package-lock.json',
7
+ 'yarn.lock',
8
+ 'pnpm-lock.yaml',
9
+ 'Pipfile.lock',
10
+ 'poetry.lock',
11
+ 'Cargo.lock',
12
+ 'composer.lock',
13
+ 'Gemfile.lock',
14
+ 'go.sum',
15
+ ];
16
+ const BUILD_DIRS = [
17
+ 'dist/**',
18
+ 'build/**',
19
+ 'out/**',
20
+ 'target/**',
21
+ 'bin/**',
22
+ 'obj/**',
23
+ '.next/**',
24
+ '.nuxt/**',
25
+ ];
26
+ const CACHE_DIRS = [
27
+ '.cache/**',
28
+ '.pytest_cache/**',
29
+ '.mypy_cache/**',
30
+ '.gradle/**',
31
+ '.idea/**',
32
+ '.vscode/**',
33
+ ];
34
+ const DEP_DIRS = ['node_modules/**', 'vendor/**', '.venv/**', 'venv/**'];
35
+ const TEMP_FILES = ['*.log', '*.tmp', '*.temp', '*.swp'];
36
+ const GENERATED_FILES = ['*.map', '*.class', '*.o', '*.pyc', '*.dll', '*.exe'];
37
+ exports.NOISE_PATTERNS = [
38
+ ...LOCK_FILES,
39
+ ...BUILD_DIRS,
40
+ ...CACHE_DIRS,
41
+ ...DEP_DIRS,
42
+ ...TEMP_FILES,
43
+ ...GENERATED_FILES,
44
+ ];
45
+ const minimatch_1 = require("minimatch");
46
+ const isNoiseFile = (file) => {
47
+ return exports.NOISE_PATTERNS.some((pattern) => (0, minimatch_1.minimatch)(file, pattern));
48
+ };
49
+ exports.isNoiseFile = isNoiseFile;
50
+ const filterNoiseFiles = (files) => {
51
+ const meaningful = files.filter((f) => !(0, exports.isNoiseFile)(f));
52
+ return meaningful.length ? meaningful : files;
53
+ };
54
+ exports.filterNoiseFiles = filterNoiseFiles;
@@ -0,0 +1,25 @@
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.printFileList = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const helper_1 = require("./helper");
9
+ const printFileList = (filesList) => {
10
+ filesList.forEach(({ status, file }) => {
11
+ if (status === '??' || status === 'A') {
12
+ (0, helper_1.log)(chalk_1.default.green(file));
13
+ }
14
+ else if (status === 'M') {
15
+ (0, helper_1.log)(chalk_1.default.yellow(file));
16
+ }
17
+ else if (status === 'D') {
18
+ (0, helper_1.log)(chalk_1.default.red(file));
19
+ }
20
+ else {
21
+ (0, helper_1.log)(file);
22
+ }
23
+ });
24
+ };
25
+ exports.printFileList = printFileList;
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pRPrompt = exports.buildBranchPromptGemma = exports.buildCommitPromptGemma = exports.buildBranchPrompt = exports.buildCommitPrompt = void 0;
4
+ const buildCommitPrompt = (summary) => {
5
+ const formatted = summary
6
+ .map((s) => `
7
+ File: ${s.file}
8
+ +${s.additions} -${s.deletions}
9
+ Signals: ${s.signals.join(', ')}
10
+
11
+ Snippet:
12
+ ${s.snippet.join('\n')}
13
+ `)
14
+ .join('\n');
15
+ return `
16
+ Generate a conventional commit message.
17
+
18
+ Rules:
19
+ - one line
20
+ - under 72 characters
21
+ - conventional commit format
22
+
23
+ Changes:
24
+ ${formatted}
25
+ `;
26
+ };
27
+ exports.buildCommitPrompt = buildCommitPrompt;
28
+ const buildBranchPrompt = (summary, existingBranches, currentBranch, commitMessage) => {
29
+ const changeList = summary
30
+ .map((s, i) => {
31
+ const signals = s.signals.length ? s.signals.join(', ') : 'code change';
32
+ return `${i + 1}. ${s.file} : ${signals}`;
33
+ })
34
+ .join('\n');
35
+ const branchList = existingBranches.slice(0, 30).join('\n'); // limit tokens
36
+ return `
37
+ You are a Git expert.
38
+
39
+ Generate a short git branch name that summarizes the main intent of the change.
40
+ Focus on the most important modification.
41
+
42
+ Rules:
43
+ - use kebab-case
44
+ - max 40 characters
45
+ - prefix with:
46
+ feature/
47
+ fix/
48
+ refactor/
49
+ chore/
50
+ docs/
51
+ - avoid existing branch names
52
+ - return ONLY the branch name
53
+ - no explanation
54
+ - no quotes
55
+
56
+ Current commit message:
57
+ ${commitMessage}
58
+
59
+ Current branch:
60
+ ${currentBranch}
61
+
62
+ Existing branches:
63
+ ${branchList}
64
+
65
+ Changes:
66
+ ${changeList}
67
+
68
+ Examples:
69
+ feature/add-commit-generator
70
+ fix/git-diff-parser
71
+ refactor/commit-analysis-pipeline
72
+ chore/update-config
73
+ docs/update-readme
74
+ `;
75
+ };
76
+ exports.buildBranchPrompt = buildBranchPrompt;
77
+ const buildCommitPromptGemma = (summary) => {
78
+ const formatted = summary
79
+ .map((s) => `
80
+ File: ${s.file}
81
+ Additions: ${s.additions}
82
+ Deletions: ${s.deletions}
83
+ Signals: ${s.signals.join(', ')}
84
+ `)
85
+ .join('\n');
86
+ return `
87
+ You generate git commit messages.
88
+
89
+ Task:
90
+ Write a Conventional Commit message describing the changes.
91
+
92
+ Rules:
93
+ - one line only
94
+ - under 72 characters
95
+ - imperative tense
96
+ - lowercase commit type
97
+ - no explanation
98
+ - no additional text
99
+
100
+ Format:
101
+ type: message
102
+
103
+ Examples:
104
+ feat: add commit generation using AI
105
+ fix: correct git diff parsing
106
+ refactor: simplify diff analyzer logic
107
+ chore: update dependencies
108
+
109
+ Changes:
110
+ ${formatted}
111
+
112
+ Output exactly one line like:
113
+ feat: add ollama integration
114
+ `;
115
+ };
116
+ exports.buildCommitPromptGemma = buildCommitPromptGemma;
117
+ const buildBranchPromptGemma = (summary, existingBranches, currentBranch, commitMessage) => {
118
+ const changeList = summary
119
+ .map((s, i) => {
120
+ const signals = s.signals.length ? s.signals.join(', ') : 'code change';
121
+ return `${i + 1}. ${s.file}: ${signals}`;
122
+ })
123
+ .join('\n');
124
+ const branchList = existingBranches.slice(0, 30).join('\n');
125
+ return `
126
+ You generate git branch names.
127
+
128
+ Goal:
129
+ Create a concise branch name describing the change.
130
+
131
+ Rules:
132
+ - kebab-case
133
+ - max 40 characters
134
+ - must start with:
135
+ feature/
136
+ fix/
137
+ refactor/
138
+ chore/
139
+ docs/
140
+ - do not match existing branches
141
+ - no explanation
142
+ - no quotes
143
+
144
+ Commit message:
145
+ ${commitMessage}
146
+
147
+ Current branch:
148
+ ${currentBranch}
149
+
150
+ Existing branches:
151
+ ${branchList}
152
+
153
+ Changed files:
154
+ ${changeList}
155
+
156
+ Output format:
157
+ feature/branch-name
158
+
159
+ Examples:
160
+ feature/add-commit-generator
161
+ fix/git-diff-parser
162
+ refactor/commit-analysis-pipeline
163
+ chore/update-config
164
+ docs/update-readme
165
+
166
+ Return ONLY the branch name.
167
+ `;
168
+ };
169
+ exports.buildBranchPromptGemma = buildBranchPromptGemma;
170
+ const pRPrompt = ({ commitMessage, branchName, summary, }) => {
171
+ return `
172
+ You are an expert software engineer.
173
+
174
+ Based on the following code changes, generate a high-quality pull request.
175
+
176
+ Inputs:
177
+ - Commit message: ${commitMessage}
178
+ - Branch name: ${branchName}
179
+ - File changes:
180
+ ${JSON.stringify(summary, null, 2)}
181
+
182
+ Instructions:
183
+ - Write a clear and concise PR title
184
+ - Write a structured PR description
185
+ - Do NOT include unnecessary explanations
186
+ - Do NOT repeat the same information
187
+ - Keep it professional and minimal
188
+ - Keep total description under 120 words
189
+
190
+ Output format:
191
+
192
+ TITLE:
193
+ <one-line PR title>
194
+
195
+ DESCRIPTION:
196
+ ## Summary
197
+ <what this PR does>
198
+
199
+ ## Changes
200
+ <bullet list of key changes>
201
+
202
+ ## Notes
203
+ <optional, only if needed>
204
+ `;
205
+ };
206
+ exports.pRPrompt = pRPrompt;
@@ -0,0 +1,17 @@
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 startCommit_1 = __importDefault(require("../commands/commit/startCommit"));
7
+ const git_1 = require("./git");
8
+ exports.default = async (payload) => {
9
+ console.log({ payload });
10
+ if (payload?.length) {
11
+ await (0, git_1.stageFiles)(payload);
12
+ }
13
+ else {
14
+ await (0, git_1.stageAll)();
15
+ }
16
+ await (0, startCommit_1.default)();
17
+ };
@@ -0,0 +1,35 @@
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("./helper");
8
+ const inputs_1 = require("./inputs");
9
+ exports.default = async (args) => {
10
+ // `args` now contains elegantly parsed flags from minimist!
11
+ // Example: `--model --local connection.json` becomes `{ model: true, local: 'connection.json' }`
12
+ // Example: `--user-model local` becomes `{ 'user-model': 'local' }`
13
+ console.log('args');
14
+ if (args['show']) {
15
+ console.log('SHOWING');
16
+ return;
17
+ }
18
+ if (args['add-key']) {
19
+ const apiKey = await (0, inputs_1.askApiKey)();
20
+ (0, helper_1.saveApiKey)(apiKey);
21
+ (0, helper_1.log)('API key saved!');
22
+ }
23
+ else if (args['delete-key']) {
24
+ if ((0, helper_1.deleteApiKey)()) {
25
+ (0, helper_1.log)(chalk_1.default.green('API Key Deleted'));
26
+ }
27
+ else {
28
+ (0, helper_1.log)(chalk_1.default.red('API Key Could Not Be Deleted. API KEY NOT FOUND!'));
29
+ }
30
+ }
31
+ else {
32
+ (0, helper_1.log)(chalk_1.default.yellow('Unrecognized config option. Here are the extracted args for your logic:'));
33
+ console.log(args);
34
+ }
35
+ };
@@ -0,0 +1,106 @@
1
+ # AI-Ship CLI Commands
2
+
3
+ AI-Ship is a CLI tool that uses AI + Git diff intelligence to automate commits and branches.
4
+
5
+ This document outlines all the available commands and flags you can use with the `ai-ship` CLI.
6
+
7
+ ## Core Commands
8
+
9
+ ### `ai-ship commit`
10
+
11
+ Generates an AI-powered commit message based on your staged Git changes, automatically commits the changes, and optionally generates and checks out a new branch based on the commit context.
12
+
13
+ AI-Ship analyzes actual Git diffs (not just filenames) to generate context-aware commit messages.
14
+
15
+ **Usage:**
16
+
17
+ ```bash
18
+ ai-ship commit [file1] [file2] ... [flags]
19
+ ```
20
+
21
+ _Note: If no files are specified, it will stage all changed files (`git add .`). If files are specified, only those specific files are staged before running the commit process._
22
+
23
+ **Available Flags:**
24
+
25
+ - `--model <provider>`: Overrides your default config to use a specific AI provider for this run (e.g., `--model local` to use Ollama/Gemma, or `--model cloud` for Gemini).
26
+ - `--new-branch`: Automatically generates a relevant branch name from the AI analysis, creates the new branch, and checks it out.
27
+ - `--push`: Automatically pushes the committed changes to your remote tracking repository. If an upstream branch is not found, it automatically creates one for you (`git push --set-upstream origin <branchName>`).
28
+ - `--yes`: Skips the interactive prompts for both commit message generation and branch creation, automatically accepting the first AI-generated suggestion.
29
+ - `--dry-run`: Simulates the process. It will generate the commit message and branch name, but will intentionally skip actually committing the files and creating the branch. Also unstages files if they were tracked during the command run.
30
+
31
+ ### `ai-ship config`
32
+
33
+ Manages the global configuration for your `ai-ship` setup (such as API keys, default models, and user preferences).
34
+
35
+ **Usage:**
36
+
37
+ ```bash
38
+ ai-ship config <sub-command> [flags]
39
+ ```
40
+
41
+ #### `config set`
42
+
43
+ Sets a specific configuration key.
44
+
45
+ **Usage:**
46
+
47
+ ```bash
48
+ ai-ship config set <key> <value>
49
+ ```
50
+
51
+ _Example: `ai-ship config set default-model local`_
52
+
53
+ #### `config get`
54
+
55
+ Retrieves the value of a specific configuration key.
56
+
57
+ **Usage:**
58
+
59
+ ```bash
60
+ ai-ship config get <key>
61
+ ```
62
+
63
+ #### `config show`
64
+
65
+ Displays your entire active configuration.
66
+
67
+ **Available Flags:**
68
+
69
+ - `--verbose`: Pretty-prints the config with color and readable formatting.
70
+ - `--json`: Prints the config as raw JSON.
71
+
72
+ #### Configuration Flags
73
+
74
+ Other utility flags applicable when invoking `ai-ship config`:
75
+
76
+ - `--add-key`: Interactively prompts you to add your AI Provider API key (e.g. Gemini API Key).
77
+ - `--delete-key`: Instantly removes your stored API key.
78
+
79
+ ## Quick Examples
80
+
81
+ **1. Fast auto-commit:**
82
+ Stages everything, generates a message, generates a branch and skips all interactive approvals.
83
+
84
+ ```bash
85
+ ai-ship commit --new-branch --yes
86
+ ```
87
+
88
+ **2. Preview what the AI thinks:**
89
+ See the AI-generated summary without altering the Git history.
90
+
91
+ ```bash
92
+ ai-ship commit --dry-run
93
+ ```
94
+
95
+ **3. Override the default model:**
96
+ Normally you use cloud, but today you want to test generating with your local Ollama setup.
97
+
98
+ ```bash
99
+ ai-ship commit --model local
100
+ ```
101
+
102
+ ## Coming Soon
103
+
104
+ ### `ai-ship pr`
105
+
106
+ Generate pull request titles and descriptions using commit history and diff analysis.