ai-pack-cli 0.1.0__tar.gz → 0.1.2__tar.gz
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.
- ai_pack_cli-0.1.2/PKG-INFO +153 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/README.md +11 -6
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack.py +21 -12
- ai_pack_cli-0.1.2/ai_pack_cli.egg-info/PKG-INFO +153 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/SOURCES.txt +5 -1
- ai_pack_cli-0.1.2/setup.py +30 -0
- ai_pack_cli-0.1.2/tests/test_binary.py +33 -0
- ai_pack_cli-0.1.2/tests/test_gitignore.py +38 -0
- ai_pack_cli-0.1.2/tests/test_skeleton_braces.py +139 -0
- ai_pack_cli-0.1.2/tests/test_skeleton_python.py +63 -0
- ai_pack_cli-0.1.0/PKG-INFO +0 -5
- ai_pack_cli-0.1.0/ai_pack_cli.egg-info/PKG-INFO +0 -5
- ai_pack_cli-0.1.0/setup.py +0 -18
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/dependency_links.txt +0 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/entry_points.txt +0 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/requires.txt +0 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/top_level.txt +0 -0
- {ai_pack_cli-0.1.0 → ai_pack_cli-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ai-pack-cli
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Home-page: https://github.com/iamraydoan/ai-pack
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
10
|
+
Requires-Dist: questionary>=1.10.0
|
|
11
|
+
|
|
12
|
+
# 📦 ai-pack
|
|
13
|
+
|
|
14
|
+
[](https://www.python.org/)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
[](https://badge.fury.io/py/ai-pack-cli)
|
|
17
|
+
[](https://github.com/)
|
|
18
|
+
[](https://github.com/iamraydoan/ai-pack)
|
|
19
|
+
|
|
20
|
+
> Pack your entire codebase into a single formatted Markdown prompt for LLMs, optimized for minimum tokens.
|
|
21
|
+
|
|
22
|
+
`ai-pack` is a lightweight, high-performance CLI tool designed to help developers package their codebase, specific files, or uncommitted changes into a clean Markdown payload. Easily copy it to the clipboard or save it to a file to feed directly into ChatGPT, Claude, Gemini, or any other LLM.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🔥 Key Features
|
|
27
|
+
|
|
28
|
+
* **⚡ Native CLI Command**: Accessible globally as `ai-pack`, `aipack`, or `aip`.
|
|
29
|
+
* **💀 Code Skeleton Extraction (`--skeleton`)**: Drastically save tokens by stripping method and function bodies, keeping only class structures, imports, signatures, and docstrings.
|
|
30
|
+
* **🎯 Interactive Selection (`-i`)**: Interactively choose which files to pack using Arrow keys and Spacebar before generating the payload.
|
|
31
|
+
* **🌿 Git-Aware (`--changed`)**: Automatically detect and pack only modified, staged, or untracked files.
|
|
32
|
+
* **🛡️ Gitignore Respecting**: Native Git integration using `git ls-files` to automatically ignore build artifacts, node modules, and everything in `.gitignore` (with a clean manual fallback for non-git folders).
|
|
33
|
+
* **💬 Predefined LLM Prompts (`-p`)**: Instantly prepend pre-configured prompts for code review, bug hunting, or architecture explanations.
|
|
34
|
+
* **📊 Token Estimation**: Heuristic token counting warns you if your payload exceeds your limit (`--max-tokens`).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
### Installation
|
|
41
|
+
|
|
42
|
+
Choose one of the following methods to get started quickly:
|
|
43
|
+
|
|
44
|
+
#### Option 1: Install from PyPI (Recommended)
|
|
45
|
+
Install the official release from PyPI:
|
|
46
|
+
```bash
|
|
47
|
+
pip3 install ai-pack-cli --user
|
|
48
|
+
```
|
|
49
|
+
Or via `pipx` to run in an isolated environment:
|
|
50
|
+
```bash
|
|
51
|
+
pipx install ai-pack-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Option 2: Install directly from GitHub
|
|
55
|
+
Install the latest cutting-edge development version directly:
|
|
56
|
+
```bash
|
|
57
|
+
pip3 install git+https://github.com/iamraydoan/ai-pack.git --user
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Option 3: Manual Clone (Editable mode)
|
|
61
|
+
If you want to modify the source code:
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/iamraydoan/ai-pack.git
|
|
64
|
+
cd ai-pack
|
|
65
|
+
pip3 install -e . --user
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Option 4: Run as a Standalone Script (One-Liner)
|
|
69
|
+
Since `ai-pack` is a self-contained single script, you can download it directly:
|
|
70
|
+
```bash
|
|
71
|
+
curl -o ~/.local/bin/aip https://raw.githubusercontent.com/iamraydoan/ai-pack/main/ai_pack.py && chmod +x ~/.local/bin/aip
|
|
72
|
+
```
|
|
73
|
+
*(Note: If you run it standalone, you will need to manually run `pip3 install pyperclip questionary` to enable all optional interactive and clipboard features).*
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
### Usage Examples
|
|
77
|
+
|
|
78
|
+
#### 1. Pack the entire repository (copied to clipboard)
|
|
79
|
+
```bash
|
|
80
|
+
aip
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### 2. Pack specific files and save to a file
|
|
84
|
+
```bash
|
|
85
|
+
aip -f src/main.py tests/ -o output.md
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### 3. Pack only uncommitted git changes with a code review prompt
|
|
89
|
+
```bash
|
|
90
|
+
aip --changed -p review
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### 4. Extract code skeleton only (drastically saves context window tokens)
|
|
94
|
+
```bash
|
|
95
|
+
aip --skeleton -p explain
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### 5. Interactively choose files to include
|
|
99
|
+
```bash
|
|
100
|
+
aip -i
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 💀 Skeleton Mode Demo
|
|
106
|
+
|
|
107
|
+
### Original Code (`math.py`):
|
|
108
|
+
```python
|
|
109
|
+
def fibonacci(n):
|
|
110
|
+
if n <= 0:
|
|
111
|
+
return []
|
|
112
|
+
elif n == 1:
|
|
113
|
+
return [0]
|
|
114
|
+
sequence = [0, 1]
|
|
115
|
+
while len(sequence) < n:
|
|
116
|
+
sequence.append(sequence[-1] + sequence[-2])
|
|
117
|
+
return sequence
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Skeleton Output:
|
|
121
|
+
```python
|
|
122
|
+
def fibonacci(n):
|
|
123
|
+
...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
*Supports Python and all brace-delimited languages (JS, TS, Go, Rust, C++, Java, Swift, etc.)*
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🎨 Interactive CLI Checklist
|
|
131
|
+
|
|
132
|
+
Running `aip -i` triggers a beautiful checkbox prompt:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
? Select files to pack (Space to toggle, Enter to confirm):
|
|
136
|
+
❯ [x] src/main.py
|
|
137
|
+
[x] src/utils.py
|
|
138
|
+
[ ] tests/test_main.py
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🤝 Contributing
|
|
144
|
+
|
|
145
|
+
Contributions are welcome! If you have ideas for new features, bug fixes, or enhancements:
|
|
146
|
+
|
|
147
|
+
1. Fork the repo.
|
|
148
|
+
2. Create your feature branch (`git checkout -b feat/amazing-feature`).
|
|
149
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`).
|
|
150
|
+
4. Push to the branch (`git push origin feat/amazing-feature`).
|
|
151
|
+
5. Open a Pull Request.
|
|
152
|
+
|
|
153
|
+
**Don't forget to give the project a ⭐ if you found it useful!**
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.python.org/)
|
|
4
4
|
[](LICENSE)
|
|
5
|
+
[](https://badge.fury.io/py/ai-pack-cli)
|
|
5
6
|
[](https://github.com/)
|
|
6
7
|
[](https://github.com/iamraydoan/ai-pack)
|
|
7
8
|
|
|
@@ -29,16 +30,20 @@
|
|
|
29
30
|
|
|
30
31
|
Choose one of the following methods to get started quickly:
|
|
31
32
|
|
|
32
|
-
#### Option 1: Install
|
|
33
|
-
|
|
33
|
+
#### Option 1: Install from PyPI (Recommended)
|
|
34
|
+
Install the official release from PyPI:
|
|
34
35
|
```bash
|
|
35
|
-
pip3 install
|
|
36
|
+
pip3 install ai-pack-cli --user
|
|
37
|
+
```
|
|
38
|
+
Or via `pipx` to run in an isolated environment:
|
|
39
|
+
```bash
|
|
40
|
+
pipx install ai-pack-cli
|
|
36
41
|
```
|
|
37
42
|
|
|
38
|
-
#### Option 2:
|
|
39
|
-
|
|
43
|
+
#### Option 2: Install directly from GitHub
|
|
44
|
+
Install the latest cutting-edge development version directly:
|
|
40
45
|
```bash
|
|
41
|
-
|
|
46
|
+
pip3 install git+https://github.com/iamraydoan/ai-pack.git --user
|
|
42
47
|
```
|
|
43
48
|
|
|
44
49
|
#### Option 3: Manual Clone (Editable mode)
|
|
@@ -231,10 +231,16 @@ class SkeletonExtractor:
|
|
|
231
231
|
indent = len(line) - len(line.lstrip())
|
|
232
232
|
sig_lines = []
|
|
233
233
|
|
|
234
|
-
# Consume multi-line signatures
|
|
234
|
+
# Consume multi-line signatures by tracking open parentheses/brackets
|
|
235
|
+
open_braces = 0
|
|
235
236
|
while i < n:
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
line_to_add = lines[i]
|
|
238
|
+
sig_lines.append(line_to_add)
|
|
239
|
+
open_braces += line_to_add.count('(') - line_to_add.count(')')
|
|
240
|
+
open_braces += line_to_add.count('[') - line_to_add.count(']')
|
|
241
|
+
open_braces += line_to_add.count('{') - line_to_add.count('}')
|
|
242
|
+
|
|
243
|
+
if open_braces <= 0 and ":" in line_to_add:
|
|
238
244
|
break
|
|
239
245
|
i += 1
|
|
240
246
|
|
|
@@ -360,15 +366,18 @@ class SkeletonExtractor:
|
|
|
360
366
|
is_func = True
|
|
361
367
|
elif any(x in prefix_str for x in ('fn ', 'func ')):
|
|
362
368
|
is_func = True
|
|
363
|
-
elif
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
elif '(' in prefix_str and ')' in prefix_str:
|
|
370
|
+
first_paren = prefix_str.find('(')
|
|
371
|
+
name_part = prefix_str[:first_paren].strip()
|
|
372
|
+
words = name_part.split()
|
|
373
|
+
if words:
|
|
374
|
+
clean_words = [w for w in words if w not in (
|
|
375
|
+
'export', 'async', 'default', 'public', 'private', 'protected', 'static', 'readonly'
|
|
376
|
+
)]
|
|
377
|
+
if clean_words:
|
|
378
|
+
first_word = clean_words[0].strip()
|
|
379
|
+
if first_word not in ('if', 'for', 'while', 'switch', 'catch'):
|
|
380
|
+
is_func = True
|
|
372
381
|
|
|
373
382
|
if is_func:
|
|
374
383
|
skip_until_brace_depth = brace_depth
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ai-pack-cli
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Home-page: https://github.com/iamraydoan/ai-pack
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
10
|
+
Requires-Dist: questionary>=1.10.0
|
|
11
|
+
|
|
12
|
+
# 📦 ai-pack
|
|
13
|
+
|
|
14
|
+
[](https://www.python.org/)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
[](https://badge.fury.io/py/ai-pack-cli)
|
|
17
|
+
[](https://github.com/)
|
|
18
|
+
[](https://github.com/iamraydoan/ai-pack)
|
|
19
|
+
|
|
20
|
+
> Pack your entire codebase into a single formatted Markdown prompt for LLMs, optimized for minimum tokens.
|
|
21
|
+
|
|
22
|
+
`ai-pack` is a lightweight, high-performance CLI tool designed to help developers package their codebase, specific files, or uncommitted changes into a clean Markdown payload. Easily copy it to the clipboard or save it to a file to feed directly into ChatGPT, Claude, Gemini, or any other LLM.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🔥 Key Features
|
|
27
|
+
|
|
28
|
+
* **⚡ Native CLI Command**: Accessible globally as `ai-pack`, `aipack`, or `aip`.
|
|
29
|
+
* **💀 Code Skeleton Extraction (`--skeleton`)**: Drastically save tokens by stripping method and function bodies, keeping only class structures, imports, signatures, and docstrings.
|
|
30
|
+
* **🎯 Interactive Selection (`-i`)**: Interactively choose which files to pack using Arrow keys and Spacebar before generating the payload.
|
|
31
|
+
* **🌿 Git-Aware (`--changed`)**: Automatically detect and pack only modified, staged, or untracked files.
|
|
32
|
+
* **🛡️ Gitignore Respecting**: Native Git integration using `git ls-files` to automatically ignore build artifacts, node modules, and everything in `.gitignore` (with a clean manual fallback for non-git folders).
|
|
33
|
+
* **💬 Predefined LLM Prompts (`-p`)**: Instantly prepend pre-configured prompts for code review, bug hunting, or architecture explanations.
|
|
34
|
+
* **📊 Token Estimation**: Heuristic token counting warns you if your payload exceeds your limit (`--max-tokens`).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
### Installation
|
|
41
|
+
|
|
42
|
+
Choose one of the following methods to get started quickly:
|
|
43
|
+
|
|
44
|
+
#### Option 1: Install from PyPI (Recommended)
|
|
45
|
+
Install the official release from PyPI:
|
|
46
|
+
```bash
|
|
47
|
+
pip3 install ai-pack-cli --user
|
|
48
|
+
```
|
|
49
|
+
Or via `pipx` to run in an isolated environment:
|
|
50
|
+
```bash
|
|
51
|
+
pipx install ai-pack-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Option 2: Install directly from GitHub
|
|
55
|
+
Install the latest cutting-edge development version directly:
|
|
56
|
+
```bash
|
|
57
|
+
pip3 install git+https://github.com/iamraydoan/ai-pack.git --user
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Option 3: Manual Clone (Editable mode)
|
|
61
|
+
If you want to modify the source code:
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/iamraydoan/ai-pack.git
|
|
64
|
+
cd ai-pack
|
|
65
|
+
pip3 install -e . --user
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Option 4: Run as a Standalone Script (One-Liner)
|
|
69
|
+
Since `ai-pack` is a self-contained single script, you can download it directly:
|
|
70
|
+
```bash
|
|
71
|
+
curl -o ~/.local/bin/aip https://raw.githubusercontent.com/iamraydoan/ai-pack/main/ai_pack.py && chmod +x ~/.local/bin/aip
|
|
72
|
+
```
|
|
73
|
+
*(Note: If you run it standalone, you will need to manually run `pip3 install pyperclip questionary` to enable all optional interactive and clipboard features).*
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
### Usage Examples
|
|
77
|
+
|
|
78
|
+
#### 1. Pack the entire repository (copied to clipboard)
|
|
79
|
+
```bash
|
|
80
|
+
aip
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### 2. Pack specific files and save to a file
|
|
84
|
+
```bash
|
|
85
|
+
aip -f src/main.py tests/ -o output.md
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### 3. Pack only uncommitted git changes with a code review prompt
|
|
89
|
+
```bash
|
|
90
|
+
aip --changed -p review
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### 4. Extract code skeleton only (drastically saves context window tokens)
|
|
94
|
+
```bash
|
|
95
|
+
aip --skeleton -p explain
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### 5. Interactively choose files to include
|
|
99
|
+
```bash
|
|
100
|
+
aip -i
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 💀 Skeleton Mode Demo
|
|
106
|
+
|
|
107
|
+
### Original Code (`math.py`):
|
|
108
|
+
```python
|
|
109
|
+
def fibonacci(n):
|
|
110
|
+
if n <= 0:
|
|
111
|
+
return []
|
|
112
|
+
elif n == 1:
|
|
113
|
+
return [0]
|
|
114
|
+
sequence = [0, 1]
|
|
115
|
+
while len(sequence) < n:
|
|
116
|
+
sequence.append(sequence[-1] + sequence[-2])
|
|
117
|
+
return sequence
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Skeleton Output:
|
|
121
|
+
```python
|
|
122
|
+
def fibonacci(n):
|
|
123
|
+
...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
*Supports Python and all brace-delimited languages (JS, TS, Go, Rust, C++, Java, Swift, etc.)*
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🎨 Interactive CLI Checklist
|
|
131
|
+
|
|
132
|
+
Running `aip -i` triggers a beautiful checkbox prompt:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
? Select files to pack (Space to toggle, Enter to confirm):
|
|
136
|
+
❯ [x] src/main.py
|
|
137
|
+
[x] src/utils.py
|
|
138
|
+
[ ] tests/test_main.py
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🤝 Contributing
|
|
144
|
+
|
|
145
|
+
Contributions are welcome! If you have ideas for new features, bug fixes, or enhancements:
|
|
146
|
+
|
|
147
|
+
1. Fork the repo.
|
|
148
|
+
2. Create your feature branch (`git checkout -b feat/amazing-feature`).
|
|
149
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`).
|
|
150
|
+
4. Push to the branch (`git push origin feat/amazing-feature`).
|
|
151
|
+
5. Open a Pull Request.
|
|
152
|
+
|
|
153
|
+
**Don't forget to give the project a ⭐ if you found it useful!**
|
|
@@ -6,4 +6,8 @@ ai_pack_cli.egg-info/SOURCES.txt
|
|
|
6
6
|
ai_pack_cli.egg-info/dependency_links.txt
|
|
7
7
|
ai_pack_cli.egg-info/entry_points.txt
|
|
8
8
|
ai_pack_cli.egg-info/requires.txt
|
|
9
|
-
ai_pack_cli.egg-info/top_level.txt
|
|
9
|
+
ai_pack_cli.egg-info/top_level.txt
|
|
10
|
+
tests/test_binary.py
|
|
11
|
+
tests/test_gitignore.py
|
|
12
|
+
tests/test_skeleton_braces.py
|
|
13
|
+
tests/test_skeleton_python.py
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
# Read the contents of README.md to use as the long description on PyPI
|
|
4
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
5
|
+
long_description = fh.read()
|
|
6
|
+
|
|
7
|
+
setup(
|
|
8
|
+
name="ai-pack-cli",
|
|
9
|
+
version="0.1.2",
|
|
10
|
+
py_modules=["ai_pack"],
|
|
11
|
+
install_requires=[
|
|
12
|
+
"pyperclip>=1.8.2",
|
|
13
|
+
"questionary>=1.10.0",
|
|
14
|
+
],
|
|
15
|
+
entry_points={
|
|
16
|
+
"console_scripts": [
|
|
17
|
+
"ai-pack=ai_pack:main",
|
|
18
|
+
"aipack=ai_pack:main",
|
|
19
|
+
"aip=ai_pack:main",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
long_description=long_description,
|
|
23
|
+
long_description_content_type="text/markdown",
|
|
24
|
+
url="https://github.com/iamraydoan/ai-pack",
|
|
25
|
+
classifiers=[
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
],
|
|
30
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
from ai_pack import is_binary
|
|
6
|
+
|
|
7
|
+
class TestBinaryDetection(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.test_dir = tempfile.mkdtemp()
|
|
10
|
+
|
|
11
|
+
def tearDown(self):
|
|
12
|
+
shutil.rmtree(self.test_dir)
|
|
13
|
+
|
|
14
|
+
def test_text_file_is_not_binary(self):
|
|
15
|
+
file_path = os.path.join(self.test_dir, "text.txt")
|
|
16
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
17
|
+
f.write("Hello, this is a plain text file with normal characters.")
|
|
18
|
+
self.assertFalse(is_binary(file_path))
|
|
19
|
+
|
|
20
|
+
def test_empty_file_is_not_binary(self):
|
|
21
|
+
file_path = os.path.join(self.test_dir, "empty.txt")
|
|
22
|
+
with open(file_path, "w") as f:
|
|
23
|
+
f.write("")
|
|
24
|
+
self.assertFalse(is_binary(file_path))
|
|
25
|
+
|
|
26
|
+
def test_binary_file_with_null_byte_is_binary(self):
|
|
27
|
+
file_path = os.path.join(self.test_dir, "binary.bin")
|
|
28
|
+
with open(file_path, "wb") as f:
|
|
29
|
+
f.write(b"PNG\x00\x00\x00\r\nIHDR")
|
|
30
|
+
self.assertTrue(is_binary(file_path))
|
|
31
|
+
|
|
32
|
+
def test_missing_file_is_treated_as_binary(self):
|
|
33
|
+
self.assertTrue(is_binary(os.path.join(self.test_dir, "does_not_exist.bin")))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
from ai_pack import GitignoreMatcher
|
|
6
|
+
|
|
7
|
+
class TestGitignoreMatcher(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.test_dir = tempfile.mkdtemp()
|
|
10
|
+
|
|
11
|
+
def tearDown(self):
|
|
12
|
+
shutil.rmtree(self.test_dir)
|
|
13
|
+
|
|
14
|
+
def test_standard_ignores(self):
|
|
15
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
16
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "node_modules", "lodash", "index.js")))
|
|
17
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "src", "__pycache__", "utils.cpython-38.pyc")))
|
|
18
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, ".git", "config")))
|
|
19
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "venv", "lib", "python3.8", "site-packages")))
|
|
20
|
+
|
|
21
|
+
def test_matching_wildcard_patterns(self):
|
|
22
|
+
with open(os.path.join(self.test_dir, ".gitignore"), "w") as f:
|
|
23
|
+
f.write("*.log\ntemp*\n")
|
|
24
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
25
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "error.log")))
|
|
26
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "sub", "output.log")))
|
|
27
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "temp_data.csv")))
|
|
28
|
+
self.assertFalse(matcher.is_ignored(os.path.join(self.test_dir, "item_temp.txt")))
|
|
29
|
+
|
|
30
|
+
def test_absolute_vs_relative_gitignore_paths(self):
|
|
31
|
+
with open(os.path.join(self.test_dir, ".gitignore"), "w") as f:
|
|
32
|
+
f.write("/root_only.txt\nsub/ignored.txt\n")
|
|
33
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
34
|
+
|
|
35
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "root_only.txt")))
|
|
36
|
+
self.assertFalse(matcher.is_ignored(os.path.join(self.test_dir, "nested", "root_only.txt")))
|
|
37
|
+
|
|
38
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "sub", "ignored.txt")))
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from ai_pack import SkeletonExtractor
|
|
3
|
+
|
|
4
|
+
class TestBraceSkeletonExtractor(unittest.TestCase):
|
|
5
|
+
def test_javascript_typescript(self):
|
|
6
|
+
js_code = """import { component } from 'framework';
|
|
7
|
+
const PORT = 8080;
|
|
8
|
+
|
|
9
|
+
export default class Widget extends Base {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super(config);
|
|
12
|
+
this.init();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
return `<div>${this.config.title}</div>`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const add = (a, b) => {
|
|
21
|
+
return a + b;
|
|
22
|
+
};
|
|
23
|
+
"""
|
|
24
|
+
expected = """import { component } from 'framework';
|
|
25
|
+
const PORT = 8080;
|
|
26
|
+
|
|
27
|
+
export default class Widget extends Base {
|
|
28
|
+
constructor(config) { /* ... */ }
|
|
29
|
+
|
|
30
|
+
render() { /* ... */ }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const add = (a, b) => { /* ... */ };"""
|
|
34
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(js_code).strip(), expected.strip())
|
|
35
|
+
|
|
36
|
+
def test_js_template_literals_with_braces(self):
|
|
37
|
+
js_code = """const render = (name) => {
|
|
38
|
+
return `Hello, ${name}! Your age is ${getAge({birth: 1990})}.`;
|
|
39
|
+
};
|
|
40
|
+
"""
|
|
41
|
+
expected = """const render = (name) => { /* ... */ };"""
|
|
42
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(js_code).strip(), expected.strip())
|
|
43
|
+
|
|
44
|
+
def test_golang_parser(self):
|
|
45
|
+
go_code = """package main
|
|
46
|
+
import "fmt"
|
|
47
|
+
|
|
48
|
+
type Server struct {
|
|
49
|
+
Port int
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func NewServer(port int) *Server {
|
|
53
|
+
return &Server{Port: port}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (s *Server) Start() error {
|
|
57
|
+
fmt.Println("Starting...")
|
|
58
|
+
return nil
|
|
59
|
+
}
|
|
60
|
+
"""
|
|
61
|
+
expected = """package main
|
|
62
|
+
import "fmt"
|
|
63
|
+
|
|
64
|
+
type Server struct {
|
|
65
|
+
Port int
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func NewServer(port int) *Server { /* ... */ }
|
|
69
|
+
|
|
70
|
+
func (s *Server) Start() error { /* ... */ }"""
|
|
71
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(go_code).strip(), expected.strip())
|
|
72
|
+
|
|
73
|
+
def test_rust_parser(self):
|
|
74
|
+
rust_code = """use std::io;
|
|
75
|
+
|
|
76
|
+
pub struct Robot {
|
|
77
|
+
name: String,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl Robot {
|
|
81
|
+
pub fn new(name: &str) -> Self {
|
|
82
|
+
Robot {
|
|
83
|
+
name: name.to_string(),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fn speak(&self) {
|
|
88
|
+
println!("Hello, my name is {}", self.name);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
"""
|
|
92
|
+
expected = """use std::io;
|
|
93
|
+
|
|
94
|
+
pub struct Robot {
|
|
95
|
+
name: String,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
impl Robot {
|
|
99
|
+
pub fn new(name: &str) -> Self { /* ... */ }
|
|
100
|
+
|
|
101
|
+
fn speak(&self) { /* ... */ }
|
|
102
|
+
}"""
|
|
103
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(rust_code).strip(), expected.strip())
|
|
104
|
+
|
|
105
|
+
def test_cpp_java_parser(self):
|
|
106
|
+
java_code = """public class App {
|
|
107
|
+
public static void main(String[] args) {
|
|
108
|
+
System.out.println("Hello World!");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private int calc(int a) {
|
|
112
|
+
return a * 2;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
"""
|
|
116
|
+
expected = """public class App {
|
|
117
|
+
public static void main(String[] args) { /* ... */ }
|
|
118
|
+
|
|
119
|
+
private int calc(int a) { /* ... */ }
|
|
120
|
+
}"""
|
|
121
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(java_code).strip(), expected.strip())
|
|
122
|
+
|
|
123
|
+
def test_typescript_return_types(self):
|
|
124
|
+
ts_code = """class Manager {
|
|
125
|
+
async getItems(req: Request): Promise<Item[]> {
|
|
126
|
+
return this.client.get();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
add(a: number, b: number): number {
|
|
130
|
+
return a + b;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
"""
|
|
134
|
+
expected = """class Manager {
|
|
135
|
+
async getItems(req: Request): Promise<Item[]> { /* ... */ }
|
|
136
|
+
|
|
137
|
+
add(a: number, b: number): number { /* ... */ }
|
|
138
|
+
}"""
|
|
139
|
+
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(ts_code).strip(), expected.strip())
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from ai_pack import SkeletonExtractor
|
|
3
|
+
|
|
4
|
+
class TestPythonSkeletonExtractor(unittest.TestCase):
|
|
5
|
+
def test_basic_functions_and_classes(self):
|
|
6
|
+
code = """class User:
|
|
7
|
+
def __init__(self, name):
|
|
8
|
+
self.name = name
|
|
9
|
+
def greet(self):
|
|
10
|
+
print("Hello " + self.name)
|
|
11
|
+
"""
|
|
12
|
+
expected = """class User:
|
|
13
|
+
def __init__(self, name):
|
|
14
|
+
...
|
|
15
|
+
def greet(self):
|
|
16
|
+
..."""
|
|
17
|
+
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
18
|
+
|
|
19
|
+
def test_decorators_and_async(self):
|
|
20
|
+
code = """@property
|
|
21
|
+
@cache
|
|
22
|
+
def value(self):
|
|
23
|
+
return self._val
|
|
24
|
+
|
|
25
|
+
async def fetch_data(url):
|
|
26
|
+
res = await client.get(url)
|
|
27
|
+
return res.json()
|
|
28
|
+
"""
|
|
29
|
+
expected = """@property
|
|
30
|
+
@cache
|
|
31
|
+
def value(self):
|
|
32
|
+
...
|
|
33
|
+
async def fetch_data(url):
|
|
34
|
+
..."""
|
|
35
|
+
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
36
|
+
|
|
37
|
+
def test_multiline_signatures(self):
|
|
38
|
+
code = """def complex_function(
|
|
39
|
+
a: int,
|
|
40
|
+
b: str,
|
|
41
|
+
c: float = 1.0
|
|
42
|
+
) -> bool:
|
|
43
|
+
print(a, b, c)
|
|
44
|
+
return True
|
|
45
|
+
"""
|
|
46
|
+
expected = """def complex_function(
|
|
47
|
+
a: int,
|
|
48
|
+
b: str,
|
|
49
|
+
c: float = 1.0
|
|
50
|
+
) -> bool:
|
|
51
|
+
..."""
|
|
52
|
+
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
53
|
+
|
|
54
|
+
def test_single_line_and_comments(self):
|
|
55
|
+
code = """def simple_get(self): return self.x
|
|
56
|
+
|
|
57
|
+
def comment_sig(a, b): # basic add
|
|
58
|
+
return a + b
|
|
59
|
+
"""
|
|
60
|
+
expected = """def simple_get(self): return self.x
|
|
61
|
+
def comment_sig(a, b): # basic add
|
|
62
|
+
..."""
|
|
63
|
+
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
ai_pack_cli-0.1.0/PKG-INFO
DELETED
ai_pack_cli-0.1.0/setup.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from setuptools import setup
|
|
2
|
-
|
|
3
|
-
setup(
|
|
4
|
-
name="ai-pack-cli",
|
|
5
|
-
version="0.1.0",
|
|
6
|
-
py_modules=["ai_pack"],
|
|
7
|
-
install_requires=[
|
|
8
|
-
"pyperclip>=1.8.2",
|
|
9
|
-
"questionary>=1.10.0",
|
|
10
|
-
],
|
|
11
|
-
entry_points={
|
|
12
|
-
"console_scripts": [
|
|
13
|
-
"ai-pack=ai_pack:main",
|
|
14
|
-
"aipack=ai_pack:main",
|
|
15
|
-
"aip=ai_pack:main",
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|