@bytespell/shella 0.1.0 → 0.1.2

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.
package/README.md CHANGED
@@ -9,11 +9,19 @@ Run multiple AI agents in parallel, persist sessions across devices, and manage
9
9
  ### npx (Developers)
10
10
 
11
11
  ```bash
12
+ # From your project directory
13
+ cd ~/myproject
12
14
  npx @bytespell/shella
13
15
  ```
14
16
 
15
17
  Opens at `http://localhost:3067`. Access from your phone using the LAN IP shown.
16
18
 
19
+ For multiple projects:
20
+
21
+ ```bash
22
+ npx @bytespell/shella --projects-dir ~/code
23
+ ```
24
+
17
25
  ### Docker (Homelab)
18
26
 
19
27
  ```yaml
@@ -24,8 +32,9 @@ services:
24
32
  ports:
25
33
  - '3067:3067'
26
34
  volumes:
27
- - /path/to/projects:/project
35
+ - ~/code:/project
28
36
  - shella-data:/root/.config/shella
37
+ command: ['--projects-dir', '/project']
29
38
  restart: unless-stopped
30
39
 
31
40
  volumes:
@@ -47,11 +56,12 @@ docker compose up -d
47
56
  ## CLI Options
48
57
 
49
58
  ```bash
50
- shella # Start with defaults
51
- shella --port 3000 # Custom web port
52
- shella --opencode-port 5000 # Custom OpenCode port
53
- shella --no-open # Don't open browser
54
- shella --help # Show help
59
+ shella # Start (registers current directory)
60
+ shella --projects-dir ~/code # Register all subdirs of ~/code
61
+ shella --port 3000 # Custom web port
62
+ shella --opencode-port 5000 # Custom OpenCode port
63
+ shella --no-open # Don't open browser
64
+ shella --help # Show help
55
65
  ```
56
66
 
57
67
  ## Development
package/bin/cli.js CHANGED
@@ -4,10 +4,17 @@ import { createOpencodeServer } from '@opencode-ai/sdk';
4
4
  import { networkInterfaces } from 'os';
5
5
  import { spawn } from 'child_process';
6
6
  import { createConnection } from 'net';
7
+ import { existsSync, readdirSync } from 'fs';
7
8
  import path from 'path';
8
9
  import { fileURLToPath } from 'url';
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
- const VERSION = '0.1.0';
11
+ const VERSION = '0.1.2';
12
+ /**
13
+ * Check if running inside Docker container
14
+ */
15
+ function isDocker() {
16
+ return existsSync('/.dockerenv');
17
+ }
11
18
  /**
12
19
  * Check if a port is available
13
20
  */
@@ -64,12 +71,63 @@ function printAccessBox(lanIp, port) {
64
71
  +${border}+
65
72
  `);
66
73
  }
74
+ /**
75
+ * Register projects with OpenCode
76
+ */
77
+ async function registerProjects(opencodePort, projectsDir) {
78
+ const baseUrl = `http://localhost:${opencodePort}`;
79
+ if (projectsDir) {
80
+ // Resolve to absolute path
81
+ const absDir = path.resolve(projectsDir);
82
+ if (!existsSync(absDir)) {
83
+ console.error(`Error: Projects directory not found: ${absDir}`);
84
+ process.exit(1);
85
+ }
86
+ // Register each subdirectory
87
+ const entries = readdirSync(absDir, { withFileTypes: true });
88
+ const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.'));
89
+ if (dirs.length === 0) {
90
+ console.error(`Error: No project directories found in ${absDir}`);
91
+ process.exit(1);
92
+ }
93
+ for (const entry of dirs) {
94
+ const dir = path.join(absDir, entry.name);
95
+ try {
96
+ await fetch(`${baseUrl}/project?directory=${encodeURIComponent(dir)}`);
97
+ console.log(` Registered: ${entry.name}`);
98
+ }
99
+ catch (err) {
100
+ console.error(` Failed to register ${entry.name}: ${err}`);
101
+ }
102
+ }
103
+ }
104
+ else {
105
+ // Register cwd
106
+ const dir = process.cwd();
107
+ try {
108
+ await fetch(`${baseUrl}/project?directory=${encodeURIComponent(dir)}`);
109
+ console.log(` Registered: ${path.basename(dir)}`);
110
+ }
111
+ catch (err) {
112
+ console.error(` Failed to register ${path.basename(dir)}: ${err}`);
113
+ }
114
+ }
115
+ }
67
116
  let opencodeServer = null;
68
117
  let expressProcess = null;
69
118
  async function startCommand(options) {
70
119
  const port = parseInt(options.port);
71
120
  const opencodePort = parseInt(options.opencodePort);
72
121
  console.log(`Shella v${VERSION}\n`);
122
+ // Docker requires --projects-dir
123
+ if (isDocker() && !options.projectsDir) {
124
+ console.error('Error: --projects-dir is required when running in Docker.\n');
125
+ console.error('Example:');
126
+ console.error(' docker run -v ~/code:/project shella --projects-dir /project\n');
127
+ console.error('Or in docker-compose.yml:');
128
+ console.error(' command: ["--projects-dir", "/project"]');
129
+ process.exit(1);
130
+ }
73
131
  // Check if ports are available
74
132
  const webPortAvailable = await isPortAvailable(port);
75
133
  const ocPortAvailable = await isPortAvailable(opencodePort);
@@ -106,6 +164,9 @@ async function startCommand(options) {
106
164
  }
107
165
  process.exit(1);
108
166
  }
167
+ // Register projects with OpenCode
168
+ console.log('Registering projects...');
169
+ await registerProjects(opencodePort, options.projectsDir);
109
170
  // Start Express server in production mode
110
171
  console.log('Starting Shella server...');
111
172
  const serverPath = path.join(__dirname, '..', 'dist', 'server', 'index.js');
@@ -186,6 +247,7 @@ program
186
247
  .description('Start Shella')
187
248
  .option('-p, --port <port>', 'Web server port', '3067')
188
249
  .option('--opencode-port <port>', 'OpenCode server port', '4096')
250
+ .option('--projects-dir <path>', 'Directory containing projects (each subdirectory becomes a project)')
189
251
  .option('--no-open', "Don't open browser automatically")
190
252
  .action(startCommand);
191
253
  program.parse();
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="4" fill="#1a1a1a"/>
3
+ <text x="16" y="23" font-family="monospace" font-size="18" font-weight="bold" fill="#22c55e" text-anchor="middle">S</text>
4
+ </svg>
@@ -2,7 +2,7 @@
2
2
  <html lang="en" class="dark">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta
7
7
  name="viewport"
8
8
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@bytespell/shella",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Self-hosted AI coding agents. Access from your phone.",
5
5
  "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/bytespell-oss/shella"
9
+ },
6
10
  "bin": {
7
- "shella": "./bin/cli.js"
11
+ "shella": "bin/cli.js"
8
12
  },
9
13
  "files": [
10
14
  "dist/",