gc-shell 0.1.0__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.
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: gc-shell
3
+ Version: 0.1.0
4
+ Summary: An intelligent macOS shell
5
+ Author: Goutham Reddy
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: rich>=13.0
10
+ Requires-Dist: prompt_toolkit>=3.0
11
+
12
+ # 🚀 Goutham Shell
13
+
14
+ A custom interactive shell for macOS that combines the power of a standard terminal with personalized built-in commands.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Clone the repository
20
+ git clone <repo-url>
21
+ cd goutham-shell
22
+
23
+ # Install in development mode
24
+ pip install -e .
25
+
26
+ # Launch the shell
27
+ goutham
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```
33
+ $ goutham
34
+ 🚀 Welcome to Goutham Shell v0.1.0
35
+ Type help for commands, exit to quit.
36
+
37
+ 💡 No app registry found. Run scan to discover installed applications.
38
+
39
+ goutham ~ ❯ scan
40
+ 🔍 Scanning for applications...
41
+ ✓ Found 142 applications
42
+ ✓ Registry saved to ~/.goutham/apps.json
43
+
44
+ goutham ~ ❯ open chrome
45
+ ▶ Opening Google Chrome...
46
+
47
+ goutham ~ ❯ running
48
+ ┌──────────────────────────────┐
49
+ │ Running Applications │
50
+ ├────┬─────────────────────────┤
51
+ │ # │ Application │
52
+ ├────┼─────────────────────────┤
53
+ │ 1 │ Finder │
54
+ │ 2 │ Google Chrome │
55
+ │ 3 │ Terminal │
56
+ └────┴─────────────────────────┘
57
+
58
+ goutham ~ ❯ ls -la
59
+ total 32
60
+ drwxr-xr-x 8 user staff 256 Jun 26 12:00 .
61
+ ...
62
+ ```
63
+
64
+ ## Features
65
+
66
+ ### System Commands
67
+
68
+ All standard terminal commands work transparently — they're forwarded to your system's `zsh` shell:
69
+
70
+ - `ls`, `pwd`, `cat`, `grep`, `echo`
71
+ - `git status`, `git commit`, `git push`
72
+ - `python app.py`, `node server.js`
73
+ - `docker ps`, `npm run dev`
74
+ - Pipes (`|`), redirection (`>`), chaining (`&&`)
75
+
76
+ ### Custom Commands
77
+
78
+ | Command | Description |
79
+ |---------|-------------|
80
+ | `scan` | Scan /Applications and build the app registry |
81
+ | `open <app>` | Open an application by name (fuzzy matching) |
82
+ | `close <app>` | Close a running application |
83
+ | `apps` | List all registered applications |
84
+ | `running` | List currently running GUI applications |
85
+ | `tree [path]` | Display a directory tree (`--depth N`) |
86
+ | `preview <file>` | Preview a file with syntax highlighting (`--lines N`) |
87
+ | `mkp <dir>` | Create nested directories (like `mkdir -p`) |
88
+ | `search <pattern>` | Find files by glob pattern (`--path <dir>`) |
89
+ | `help` | Show all available commands |
90
+ | `exit` / `quit` | Exit the shell |
91
+
92
+ ### App Management
93
+
94
+ On first run, use `scan` to discover installed applications. Then use `open` and `close` with **fuzzy name matching** — no need to type the full name:
95
+
96
+ ```
97
+ goutham ~ ❯ open chrome # Opens "Google Chrome"
98
+ goutham ~ ❯ open code # Opens "Visual Studio Code"
99
+ goutham ~ ❯ close spotify # Closes "Spotify"
100
+ ```
101
+
102
+ The `open` command is smart — paths, URLs, and flags are forwarded to the system `open`:
103
+
104
+ ```
105
+ goutham ~ ❯ open . # Opens current dir in Finder
106
+ goutham ~ ❯ open https://x.com # Opens URL in default browser
107
+ goutham ~ ❯ open -a Safari # System open with flags
108
+ ```
109
+
110
+ ## Architecture
111
+
112
+ Goutham Shell is a **wrapper shell** — it intercepts custom commands while forwarding everything else transparently to `zsh`.
113
+
114
+ ```
115
+ User Input → Command Parser → Custom Command? → Execute Handler
116
+ ↓ No
117
+ Forward to zsh via subprocess
118
+ ```
119
+
120
+ Key design decisions:
121
+
122
+ - **Shell state is maintained in-process**: `cd` updates `current_directory` so subsequent commands run in the right place.
123
+ - **No hardcoded paths**: All app paths come from the scanned registry at `~/.goutham/apps.json`.
124
+ - **`shell=True` forwarding**: Preserves pipes, redirection, globbing, and chaining — users get the full power of `zsh`.
125
+
126
+ ## Requirements
127
+
128
+ - macOS 10.15+
129
+ - Python 3.9+
130
+ - [Rich](https://github.com/Textualize/rich) (installed automatically)
131
+
132
+ ## Configuration
133
+
134
+ All configuration is stored in `~/.goutham/`:
135
+
136
+ | File | Purpose |
137
+ |------|---------|
138
+ | `apps.json` | Application registry (generated by `scan`) |
139
+
140
+ ## Project Structure
141
+
142
+ ```
143
+ goutham-shell/
144
+ ├── main.py # Entry point
145
+ ├── shell.py # Interactive REPL loop
146
+ ├── executor.py # Command routing & shell state
147
+ ├── commands/
148
+ │ ├── apps.py # open, close, apps, running
149
+ │ └── files.py # tree, preview, mkp, search
150
+ ├── registry/
151
+ │ └── scanner.py # macOS app scanner
152
+ ├── ai/ # Phase 5+ (AI commands)
153
+ ├── utils/
154
+ ├── pyproject.toml
155
+ └── README.md
156
+ ```
157
+
158
+ ## Roadmap
159
+
160
+ - [x] Phase 1 — Interactive shell skeleton
161
+ - [x] Phase 2 — System command forwarding with persistent state
162
+ - [x] Phase 3 — Custom built-in commands (app management)
163
+ - [x] Phase 4 — File utility commands
164
+ - [ ] Phase 5 — AI commands (`ai explain`, `ai generate`, `ai summarize`)
165
+ - [ ] Phase 6 — LangGraph agent workflows
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,158 @@
1
+ # 🚀 Goutham Shell
2
+
3
+ A custom interactive shell for macOS that combines the power of a standard terminal with personalized built-in commands.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Clone the repository
9
+ git clone <repo-url>
10
+ cd goutham-shell
11
+
12
+ # Install in development mode
13
+ pip install -e .
14
+
15
+ # Launch the shell
16
+ goutham
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```
22
+ $ goutham
23
+ 🚀 Welcome to Goutham Shell v0.1.0
24
+ Type help for commands, exit to quit.
25
+
26
+ 💡 No app registry found. Run scan to discover installed applications.
27
+
28
+ goutham ~ ❯ scan
29
+ 🔍 Scanning for applications...
30
+ ✓ Found 142 applications
31
+ ✓ Registry saved to ~/.goutham/apps.json
32
+
33
+ goutham ~ ❯ open chrome
34
+ ▶ Opening Google Chrome...
35
+
36
+ goutham ~ ❯ running
37
+ ┌──────────────────────────────┐
38
+ │ Running Applications │
39
+ ├────┬─────────────────────────┤
40
+ │ # │ Application │
41
+ ├────┼─────────────────────────┤
42
+ │ 1 │ Finder │
43
+ │ 2 │ Google Chrome │
44
+ │ 3 │ Terminal │
45
+ └────┴─────────────────────────┘
46
+
47
+ goutham ~ ❯ ls -la
48
+ total 32
49
+ drwxr-xr-x 8 user staff 256 Jun 26 12:00 .
50
+ ...
51
+ ```
52
+
53
+ ## Features
54
+
55
+ ### System Commands
56
+
57
+ All standard terminal commands work transparently — they're forwarded to your system's `zsh` shell:
58
+
59
+ - `ls`, `pwd`, `cat`, `grep`, `echo`
60
+ - `git status`, `git commit`, `git push`
61
+ - `python app.py`, `node server.js`
62
+ - `docker ps`, `npm run dev`
63
+ - Pipes (`|`), redirection (`>`), chaining (`&&`)
64
+
65
+ ### Custom Commands
66
+
67
+ | Command | Description |
68
+ |---------|-------------|
69
+ | `scan` | Scan /Applications and build the app registry |
70
+ | `open <app>` | Open an application by name (fuzzy matching) |
71
+ | `close <app>` | Close a running application |
72
+ | `apps` | List all registered applications |
73
+ | `running` | List currently running GUI applications |
74
+ | `tree [path]` | Display a directory tree (`--depth N`) |
75
+ | `preview <file>` | Preview a file with syntax highlighting (`--lines N`) |
76
+ | `mkp <dir>` | Create nested directories (like `mkdir -p`) |
77
+ | `search <pattern>` | Find files by glob pattern (`--path <dir>`) |
78
+ | `help` | Show all available commands |
79
+ | `exit` / `quit` | Exit the shell |
80
+
81
+ ### App Management
82
+
83
+ On first run, use `scan` to discover installed applications. Then use `open` and `close` with **fuzzy name matching** — no need to type the full name:
84
+
85
+ ```
86
+ goutham ~ ❯ open chrome # Opens "Google Chrome"
87
+ goutham ~ ❯ open code # Opens "Visual Studio Code"
88
+ goutham ~ ❯ close spotify # Closes "Spotify"
89
+ ```
90
+
91
+ The `open` command is smart — paths, URLs, and flags are forwarded to the system `open`:
92
+
93
+ ```
94
+ goutham ~ ❯ open . # Opens current dir in Finder
95
+ goutham ~ ❯ open https://x.com # Opens URL in default browser
96
+ goutham ~ ❯ open -a Safari # System open with flags
97
+ ```
98
+
99
+ ## Architecture
100
+
101
+ Goutham Shell is a **wrapper shell** — it intercepts custom commands while forwarding everything else transparently to `zsh`.
102
+
103
+ ```
104
+ User Input → Command Parser → Custom Command? → Execute Handler
105
+ ↓ No
106
+ Forward to zsh via subprocess
107
+ ```
108
+
109
+ Key design decisions:
110
+
111
+ - **Shell state is maintained in-process**: `cd` updates `current_directory` so subsequent commands run in the right place.
112
+ - **No hardcoded paths**: All app paths come from the scanned registry at `~/.goutham/apps.json`.
113
+ - **`shell=True` forwarding**: Preserves pipes, redirection, globbing, and chaining — users get the full power of `zsh`.
114
+
115
+ ## Requirements
116
+
117
+ - macOS 10.15+
118
+ - Python 3.9+
119
+ - [Rich](https://github.com/Textualize/rich) (installed automatically)
120
+
121
+ ## Configuration
122
+
123
+ All configuration is stored in `~/.goutham/`:
124
+
125
+ | File | Purpose |
126
+ |------|---------|
127
+ | `apps.json` | Application registry (generated by `scan`) |
128
+
129
+ ## Project Structure
130
+
131
+ ```
132
+ goutham-shell/
133
+ ├── main.py # Entry point
134
+ ├── shell.py # Interactive REPL loop
135
+ ├── executor.py # Command routing & shell state
136
+ ├── commands/
137
+ │ ├── apps.py # open, close, apps, running
138
+ │ └── files.py # tree, preview, mkp, search
139
+ ├── registry/
140
+ │ └── scanner.py # macOS app scanner
141
+ ├── ai/ # Phase 5+ (AI commands)
142
+ ├── utils/
143
+ ├── pyproject.toml
144
+ └── README.md
145
+ ```
146
+
147
+ ## Roadmap
148
+
149
+ - [x] Phase 1 — Interactive shell skeleton
150
+ - [x] Phase 2 — System command forwarding with persistent state
151
+ - [x] Phase 3 — Custom built-in commands (app management)
152
+ - [x] Phase 4 — File utility commands
153
+ - [ ] Phase 5 — AI commands (`ai explain`, `ai generate`, `ai summarize`)
154
+ - [ ] Phase 6 — LangGraph agent workflows
155
+
156
+ ## License
157
+
158
+ MIT
File without changes
@@ -0,0 +1,202 @@
1
+ """App management commands — open, close, list apps, list running."""
2
+
3
+ import subprocess
4
+
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+
8
+ from registry.scanner import get_registry_path, load_registry
9
+
10
+
11
+ # ---------------------------------------------------------------------------
12
+ # Fuzzy matching
13
+ # ---------------------------------------------------------------------------
14
+
15
+ def _fuzzy_match(query: str, registry: dict) -> tuple | None:
16
+ """Find the best match for *query* in the registry.
17
+
18
+ Match precedence:
19
+ 1. Exact key match (e.g. "google chrome" → "google chrome")
20
+ 2. Query is a substring of a key (e.g. "chrome" → "google chrome")
21
+ 3. Key is a substring of the query
22
+ When multiple candidates tie, the shortest key wins (most specific).
23
+ """
24
+ query = query.lower().strip()
25
+
26
+ # 1. Exact match
27
+ if query in registry:
28
+ return query, registry[query]
29
+
30
+ # 2/3. Substring matches
31
+ matches = [
32
+ (key, info)
33
+ for key, info in registry.items()
34
+ if query in key or key in query
35
+ ]
36
+
37
+ if len(matches) == 1:
38
+ return matches[0]
39
+ if len(matches) > 1:
40
+ matches.sort(key=lambda x: len(x[0]))
41
+ return matches[0]
42
+
43
+ return None
44
+
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Commands
48
+ # ---------------------------------------------------------------------------
49
+
50
+ def open_app(name: str, console: Console) -> bool:
51
+ """Open an application by name.
52
+
53
+ Returns True if the app was found (whether or not it launched successfully),
54
+ False if no match was found so the caller can fall through.
55
+ """
56
+ registry = load_registry()
57
+
58
+ if not registry:
59
+ console.print(
60
+ "[yellow]⚠ No app registry found. "
61
+ "Run [bold]scan[/bold] first.[/yellow]"
62
+ )
63
+ return True # handled (with a warning), don't fall through
64
+
65
+ match = _fuzzy_match(name, registry)
66
+
67
+ if match:
68
+ _key, info = match
69
+ app_name = info["name"]
70
+ console.print(f"[green]▶[/green] Opening [bold]{app_name}[/bold]...")
71
+ try:
72
+ subprocess.run(
73
+ ["open", "-a", info["path"]],
74
+ check=True,
75
+ capture_output=True,
76
+ text=True,
77
+ )
78
+ except subprocess.CalledProcessError as e:
79
+ console.print(
80
+ f"[red]✗ Failed to open {app_name}:[/red] {e.stderr.strip()}"
81
+ )
82
+ return True
83
+
84
+ return False # not in registry — let the executor fall through
85
+
86
+
87
+ def close_app(name: str, console: Console) -> None:
88
+ """Close an application by name via AppleScript quit."""
89
+ registry = load_registry()
90
+
91
+ if not registry:
92
+ console.print(
93
+ "[yellow]⚠ No app registry found. "
94
+ "Run [bold]scan[/bold] first.[/yellow]"
95
+ )
96
+ return
97
+
98
+ match = _fuzzy_match(name, registry)
99
+
100
+ if match:
101
+ _key, info = match
102
+ app_name = info["name"]
103
+ console.print(f"[red]■[/red] Closing [bold]{app_name}[/bold]...")
104
+ try:
105
+ subprocess.run(
106
+ [
107
+ "osascript",
108
+ "-e",
109
+ f'tell application "{app_name}" to quit',
110
+ ],
111
+ check=True,
112
+ capture_output=True,
113
+ text=True,
114
+ )
115
+ except subprocess.CalledProcessError:
116
+ # Fallback: SIGTERM via pkill
117
+ try:
118
+ subprocess.run(
119
+ ["pkill", "-f", app_name], capture_output=True
120
+ )
121
+ except Exception:
122
+ console.print(f"[red]✗ Could not close {app_name}[/red]")
123
+ else:
124
+ console.print(f"[red]✗ App '{name}' not found in registry.[/red]")
125
+ console.print(
126
+ "[dim]Run [bold]scan[/bold] to update the app registry.[/dim]"
127
+ )
128
+
129
+
130
+ def list_apps(console: Console) -> None:
131
+ """Print a table of all registered applications."""
132
+ registry = load_registry()
133
+
134
+ if not registry:
135
+ console.print(
136
+ "[yellow]⚠ No app registry found. "
137
+ "Run [bold]scan[/bold] first.[/yellow]"
138
+ )
139
+ return
140
+
141
+ table = Table(
142
+ title="Registered Applications",
143
+ show_lines=False,
144
+ border_style="dim",
145
+ title_style="bold cyan",
146
+ )
147
+ table.add_column("#", style="dim", justify="right", width=4)
148
+ table.add_column("Application", style="cyan bold", no_wrap=True)
149
+ table.add_column("Path", style="dim")
150
+
151
+ for i, key in enumerate(sorted(registry.keys()), 1):
152
+ info = registry[key]
153
+ table.add_row(str(i), info["name"], info["path"])
154
+
155
+ console.print()
156
+ console.print(table)
157
+ console.print(
158
+ f"\n[dim]{len(registry)} applications registered • "
159
+ f"Registry: {get_registry_path()}[/dim]\n"
160
+ )
161
+
162
+
163
+ def list_running(console: Console) -> None:
164
+ """List currently running GUI applications via System Events."""
165
+ try:
166
+ result = subprocess.run(
167
+ [
168
+ "osascript",
169
+ "-e",
170
+ 'tell application "System Events" to get name of '
171
+ "every process whose background only is false",
172
+ ],
173
+ capture_output=True,
174
+ text=True,
175
+ check=True,
176
+ )
177
+ app_names = sorted(
178
+ name.strip() for name in result.stdout.strip().split(", ")
179
+ )
180
+
181
+ table = Table(
182
+ title="Running Applications",
183
+ show_lines=False,
184
+ border_style="dim",
185
+ title_style="bold green",
186
+ )
187
+ table.add_column("#", style="dim", justify="right", width=4)
188
+ table.add_column("Application", style="green bold", no_wrap=True)
189
+
190
+ for i, name in enumerate(app_names, 1):
191
+ table.add_row(str(i), name)
192
+
193
+ console.print()
194
+ console.print(table)
195
+ console.print(f"\n[dim]{len(app_names)} applications running[/dim]\n")
196
+
197
+ except subprocess.CalledProcessError as e:
198
+ console.print(
199
+ f"[red]✗ Failed to list running apps:[/red] {e.stderr.strip()}"
200
+ )
201
+ except Exception as e:
202
+ console.print(f"[red]✗ Error:[/red] {e}")