@_xtribe/cli 1.0.19 → 1.0.21

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/install-tribe.js CHANGED
@@ -821,16 +821,107 @@ async function startContainerRuntime() {
821
821
  // Colima not running, need to start it
822
822
  }
823
823
 
824
- // Don't try to start Colima automatically - it takes too long
824
+ // Start Colima automatically
825
825
  if (await checkCommand('colima')) {
826
- spinner.stop(); // Stop the spinner before showing info
827
- console.log('');
828
- console.log(chalk.yellow(' ⚠️ Colima needs to be started before using TRIBE'));
829
- console.log(chalk.gray(' To start Colima with Kubernetes:'));
830
- console.log(chalk.cyan(' colima start --kubernetes'));
831
- console.log(chalk.gray(' This may take 2-5 minutes on first run'));
832
- console.log('');
833
- return true; // Return true since it's installed
826
+ spinner.text = 'Starting Colima with Kubernetes (this may take a few minutes on first run)...';
827
+
828
+ // Start Colima in the background
829
+ const { spawn } = require('child_process');
830
+ const colimaProcess = spawn('colima', ['start', '--kubernetes'], {
831
+ stdio: ['ignore', 'pipe', 'pipe'],
832
+ detached: false
833
+ });
834
+
835
+ let output = '';
836
+ let errorOutput = '';
837
+ let startupComplete = false;
838
+
839
+ // Monitor output
840
+ colimaProcess.stdout.on('data', (data) => {
841
+ output += data.toString();
842
+ // Update spinner with progress
843
+ const lines = data.toString().split('\n').filter(line => line.trim());
844
+ for (const line of lines) {
845
+ if (line.includes('starting ...')) {
846
+ const contextMatch = line.match(/context=(\w+)/);
847
+ if (contextMatch) {
848
+ spinner.text = `Starting Colima: ${contextMatch[1]}...`;
849
+ }
850
+ } else if (line.includes('provisioning ...')) {
851
+ const contextMatch = line.match(/context=(\w+)/);
852
+ if (contextMatch) {
853
+ spinner.text = `Provisioning ${contextMatch[1]}...`;
854
+ }
855
+ } else if (line.includes('done')) {
856
+ startupComplete = true;
857
+ }
858
+ }
859
+ });
860
+
861
+ colimaProcess.stderr.on('data', (data) => {
862
+ errorOutput += data.toString();
863
+ // Colima outputs progress to stderr, not stdout
864
+ const lines = data.toString().split('\n').filter(line => line.trim());
865
+ for (const line of lines) {
866
+ if (line.includes('starting ...')) {
867
+ const contextMatch = line.match(/context=(\w+)/);
868
+ if (contextMatch) {
869
+ spinner.text = `Starting Colima: ${contextMatch[1]}...`;
870
+ }
871
+ } else if (line.includes('provisioning ...')) {
872
+ const contextMatch = line.match(/context=(\w+)/);
873
+ if (contextMatch) {
874
+ spinner.text = `Provisioning ${contextMatch[1]}...`;
875
+ }
876
+ } else if (line.includes('done')) {
877
+ startupComplete = true;
878
+ }
879
+ }
880
+ });
881
+
882
+ // Wait for process to complete
883
+ const exitCode = await new Promise((resolve) => {
884
+ colimaProcess.on('exit', (code) => {
885
+ resolve(code);
886
+ });
887
+ });
888
+
889
+ if (exitCode === 0 && startupComplete) {
890
+ // Verify Colima is actually running
891
+ try {
892
+ execSync('colima status', { stdio: 'ignore' });
893
+ spinner.succeed('Colima started with Kubernetes');
894
+
895
+ // Give k3s a moment to initialize
896
+ spinner.text = 'Waiting for Kubernetes to initialize...';
897
+ await new Promise(resolve => setTimeout(resolve, 5000));
898
+
899
+ return true;
900
+ } catch {
901
+ spinner.fail('Colima started but is not responding');
902
+ return false;
903
+ }
904
+ } else {
905
+ spinner.fail('Failed to start Colima');
906
+ // Only show actual error messages, not progress
907
+ const actualErrors = errorOutput.split('\n')
908
+ .filter(line => line.trim() &&
909
+ !line.includes('level=info') &&
910
+ !line.includes('starting ...') &&
911
+ !line.includes('provisioning ...') &&
912
+ !line.includes('done'))
913
+ .join('\n');
914
+
915
+ if (actualErrors.trim()) {
916
+ console.log(chalk.red('Error output:'));
917
+ console.log(actualErrors);
918
+ }
919
+ console.log('');
920
+ console.log(chalk.yellow(' Please start Colima manually:'));
921
+ console.log(chalk.cyan(' colima start --kubernetes'));
922
+ console.log('');
923
+ return false;
924
+ }
834
925
  } else {
835
926
  spinner.warn('Colima not found');
836
927
  return false;
@@ -956,9 +1047,28 @@ async function verifyInstallation() {
956
1047
 
957
1048
  async function checkClusterExists() {
958
1049
  try {
959
- // Check if TRIBE namespace exists in any context
1050
+ // Check if TRIBE namespace exists
960
1051
  execSync('kubectl get namespace tribe-system', { stdio: 'ignore' });
961
- return true;
1052
+
1053
+ // Also check if key TRIBE pods are running
1054
+ const podsOutput = execSync('kubectl get pods -n tribe-system -o json', {
1055
+ encoding: 'utf8',
1056
+ stdio: 'pipe'
1057
+ });
1058
+
1059
+ const pods = JSON.parse(podsOutput);
1060
+ if (!pods.items || pods.items.length === 0) {
1061
+ return false; // Namespace exists but no pods
1062
+ }
1063
+
1064
+ // Check if essential pods exist (gitea, postgres, taskmaster)
1065
+ const essentialPods = ['gitea', 'postgres', 'taskmaster'];
1066
+ const runningPods = pods.items.filter(pod => {
1067
+ const podName = pod.metadata.name;
1068
+ return essentialPods.some(essential => podName.includes(essential));
1069
+ });
1070
+
1071
+ return runningPods.length >= essentialPods.length;
962
1072
  } catch {
963
1073
  return false;
964
1074
  }
@@ -1320,6 +1430,17 @@ async function cleanupOldInstallations() {
1320
1430
  async function forceCleanInstallation() {
1321
1431
  console.log(chalk.yellow('\n🧹 Performing force clean installation...\n'));
1322
1432
 
1433
+ // Clean up Kubernetes resources first (while cluster is running)
1434
+ try {
1435
+ execSync('kubectl delete namespace tribe-system --ignore-not-found=true', {
1436
+ stdio: 'ignore',
1437
+ timeout: 30000
1438
+ });
1439
+ log.success('Removed TRIBE namespace from cluster');
1440
+ } catch (error) {
1441
+ // Cluster might not be running or namespace doesn't exist
1442
+ }
1443
+
1323
1444
  // Uninstall global npm package if exists
1324
1445
  try {
1325
1446
  execSync('npm uninstall -g tribe-cli-global', {
@@ -1531,21 +1652,23 @@ async function main() {
1531
1652
  const verified = await verifyInstallation();
1532
1653
 
1533
1654
  // Try to start container runtime (after showing results)
1534
- await startContainerRuntime();
1655
+ const runtimeStarted = await startContainerRuntime();
1535
1656
 
1536
- if (verified) {
1657
+ // If we successfully started the runtime, we should continue with deployment
1658
+ if (verified || runtimeStarted) {
1537
1659
  // Check if cluster already exists
1538
1660
  const clusterExists = await checkClusterExists();
1539
1661
 
1540
1662
  if (!clusterExists && !global.skipCluster) {
1541
- // Prompt for cluster setup
1542
- const shouldSetup = await promptForClusterSetup();
1663
+ // If we just started Colima, automatically deploy the cluster
1664
+ // Don't prompt if we just started the runtime - deploy automatically
1665
+ const shouldSetup = runtimeStarted ? true : await promptForClusterSetup();
1543
1666
 
1544
1667
  if (shouldSetup) {
1545
1668
  console.log('');
1546
1669
 
1547
- // Start Colima with Kubernetes if on macOS
1548
- if (platform === 'darwin') {
1670
+ // Start Colima with Kubernetes if on macOS (skip if we just started it)
1671
+ if (platform === 'darwin' && !runtimeStarted) {
1549
1672
  const colimaStarted = await startColimaWithKubernetes();
1550
1673
  if (!colimaStarted) {
1551
1674
  log.error('Failed to start Colima. Please run manually:');
@@ -1562,22 +1685,8 @@ async function main() {
1562
1685
  console.log('\n' + chalk.bold.green('✨ TRIBE is ready!'));
1563
1686
  console.log('');
1564
1687
 
1565
- // Offer to set up PATH
1566
- if (global.envScriptCreated && !process.env.CI && !process.env.TRIBE_SKIP_PATH_PROMPT) {
1567
- const shouldSetupPath = await promptForPathSetup();
1568
- if (shouldSetupPath) {
1569
- // Run the setup-path logic inline
1570
- try {
1571
- const setupPath = require('./setup-path.js');
1572
- setupPath();
1573
- console.log('');
1574
- // PATH is now set up, update instructions
1575
- global.pathSetupComplete = true;
1576
- } catch (error) {
1577
- log.warning('Could not automatically set up PATH');
1578
- }
1579
- }
1580
- }
1688
+ // PATH was already set up automatically in setupPathEnvironment
1689
+ // No need to prompt again
1581
1690
 
1582
1691
  // Provide immediate access to tribe command
1583
1692
  if (global.pathSetupComplete) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "TRIBE multi-agent development system - Zero to productive with one command",
5
5
  "main": "install-tribe.js",
6
6
  "bin": {
@@ -40,6 +40,7 @@
40
40
  "install-tribe.js",
41
41
  "setup-path.js",
42
42
  "install.sh",
43
+ "tribe-deployment.yaml",
43
44
  "README.md",
44
45
  "package.json"
45
46
  ],
@@ -0,0 +1,455 @@
1
+ ---
2
+ # TRIBE Complete Deployment - Bundled with NPM Package
3
+ # This file is automatically deployed by 'npx @_xtribe/cli'
4
+ #
5
+ # IMPORTANT: This deployment uses PUBLIC Docker Hub images from tribexal/tribe
6
+ # - tribexal/tribe:latest-taskmaster
7
+ # - tribexal/tribe:latest-bridge
8
+ # - tribexal/tribe:latest-claude-agent
9
+ #
10
+ # DO NOT change these to local image names for the npm package!
11
+ ---
12
+ apiVersion: v1
13
+ kind: Namespace
14
+ metadata:
15
+ name: tribe-system
16
+ ---
17
+ # ServiceAccount for Bridge
18
+ apiVersion: v1
19
+ kind: ServiceAccount
20
+ metadata:
21
+ name: bridge
22
+ namespace: tribe-system
23
+ ---
24
+ # Role for Bridge
25
+ apiVersion: rbac.authorization.k8s.io/v1
26
+ kind: Role
27
+ metadata:
28
+ name: bridge-role
29
+ namespace: tribe-system
30
+ rules:
31
+ - apiGroups: [""]
32
+ resources: ["pods", "services", "pods/exec", "pods/log", "configmaps", "secrets", "namespaces"]
33
+ verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
34
+ - apiGroups: ["apps"]
35
+ resources: ["deployments", "replicasets"]
36
+ verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
37
+ - apiGroups: ["batch"]
38
+ resources: ["jobs"]
39
+ verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
40
+ ---
41
+ # RoleBinding for Bridge
42
+ apiVersion: rbac.authorization.k8s.io/v1
43
+ kind: RoleBinding
44
+ metadata:
45
+ name: bridge-rolebinding
46
+ namespace: tribe-system
47
+ subjects:
48
+ - kind: ServiceAccount
49
+ name: bridge
50
+ namespace: tribe-system
51
+ roleRef:
52
+ kind: Role
53
+ name: bridge-role
54
+ apiGroup: rbac.authorization.k8s.io
55
+ ---
56
+ # PostgreSQL
57
+ apiVersion: apps/v1
58
+ kind: Deployment
59
+ metadata:
60
+ name: postgres
61
+ namespace: tribe-system
62
+ spec:
63
+ replicas: 1
64
+ selector:
65
+ matchLabels:
66
+ app: postgres
67
+ template:
68
+ metadata:
69
+ labels:
70
+ app: postgres
71
+ spec:
72
+ containers:
73
+ - name: postgres
74
+ image: postgres:15
75
+ env:
76
+ - name: POSTGRES_DB
77
+ value: gitea
78
+ - name: POSTGRES_USER
79
+ value: gitea
80
+ - name: POSTGRES_PASSWORD
81
+ value: gitea
82
+ ports:
83
+ - containerPort: 5432
84
+ readinessProbe:
85
+ exec:
86
+ command:
87
+ - pg_isready
88
+ - -U
89
+ - gitea
90
+ initialDelaySeconds: 5
91
+ periodSeconds: 5
92
+ ---
93
+ apiVersion: v1
94
+ kind: Service
95
+ metadata:
96
+ name: postgres
97
+ namespace: tribe-system
98
+ spec:
99
+ selector:
100
+ app: postgres
101
+ ports:
102
+ - port: 5432
103
+ ---
104
+ # Gitea
105
+ apiVersion: apps/v1
106
+ kind: Deployment
107
+ metadata:
108
+ name: gitea
109
+ namespace: tribe-system
110
+ spec:
111
+ replicas: 1
112
+ selector:
113
+ matchLabels:
114
+ app: gitea
115
+ template:
116
+ metadata:
117
+ labels:
118
+ app: gitea
119
+ spec:
120
+ initContainers:
121
+ - name: wait-for-db
122
+ image: busybox:1.35
123
+ command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for db; sleep 2; done']
124
+ - name: init-gitea
125
+ image: gitea/gitea:1.20.5
126
+ command: ['/bin/bash', '-c']
127
+ args:
128
+ - |
129
+ # Create app.ini
130
+ mkdir -p /data/gitea/conf
131
+ cat > /data/gitea/conf/app.ini << 'EOF'
132
+ APP_NAME = Gitea
133
+ RUN_MODE = prod
134
+
135
+ [database]
136
+ DB_TYPE = postgres
137
+ HOST = postgres:5432
138
+ NAME = gitea
139
+ USER = gitea
140
+ PASSWD = gitea
141
+
142
+ [server]
143
+ DOMAIN = gitea
144
+ ROOT_URL = http://gitea:3000/
145
+ HTTP_PORT = 3000
146
+
147
+ [service]
148
+ DISABLE_REGISTRATION = true
149
+
150
+ [security]
151
+ INSTALL_LOCK = true
152
+ SECRET_KEY = changeme
153
+ EOF
154
+
155
+ # Run migrations
156
+ gitea migrate
157
+
158
+ # Create admin user
159
+ gitea admin user create --admin --username gitea_admin --password admin123 --email admin@example.com || true
160
+ env:
161
+ - name: GITEA_WORK_DIR
162
+ value: /data
163
+ - name: GITEA_CUSTOM
164
+ value: /data/gitea
165
+ volumeMounts:
166
+ - name: gitea-data
167
+ mountPath: /data
168
+ containers:
169
+ - name: gitea
170
+ image: gitea/gitea:1.20.5
171
+ ports:
172
+ - containerPort: 3000
173
+ env:
174
+ - name: GITEA_WORK_DIR
175
+ value: /data
176
+ - name: GITEA_CUSTOM
177
+ value: /data/gitea
178
+ volumeMounts:
179
+ - name: gitea-data
180
+ mountPath: /data
181
+ readinessProbe:
182
+ httpGet:
183
+ path: /
184
+ port: 3000
185
+ initialDelaySeconds: 30
186
+ periodSeconds: 10
187
+ volumes:
188
+ - name: gitea-data
189
+ emptyDir: {}
190
+ ---
191
+ apiVersion: v1
192
+ kind: Service
193
+ metadata:
194
+ name: gitea
195
+ namespace: tribe-system
196
+ spec:
197
+ selector:
198
+ app: gitea
199
+ ports:
200
+ - port: 3000
201
+ ---
202
+ # TaskMaster
203
+ apiVersion: apps/v1
204
+ kind: Deployment
205
+ metadata:
206
+ name: taskmaster
207
+ namespace: tribe-system
208
+ spec:
209
+ replicas: 1
210
+ selector:
211
+ matchLabels:
212
+ app: taskmaster
213
+ template:
214
+ metadata:
215
+ labels:
216
+ app: taskmaster
217
+ spec:
218
+ initContainers:
219
+ - name: wait-for-db
220
+ image: busybox:1.35
221
+ command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for db; sleep 2; done']
222
+ containers:
223
+ - name: taskmaster
224
+ image: tribexal/tribe:latest-taskmaster
225
+ imagePullPolicy: IfNotPresent
226
+ ports:
227
+ - containerPort: 5000
228
+ env:
229
+ - name: FLASK_ENV
230
+ value: development
231
+ - name: DATABASE_URL
232
+ value: postgresql://gitea:gitea@postgres:5432/gitea
233
+ - name: GITEA_URL
234
+ value: http://gitea:3000
235
+ - name: GITEA_TOKEN
236
+ value: will-be-set-by-init-job
237
+ readinessProbe:
238
+ httpGet:
239
+ path: /
240
+ port: 5000
241
+ initialDelaySeconds: 10
242
+ periodSeconds: 5
243
+ ---
244
+ apiVersion: v1
245
+ kind: Service
246
+ metadata:
247
+ name: taskmaster
248
+ namespace: tribe-system
249
+ spec:
250
+ selector:
251
+ app: taskmaster
252
+ ports:
253
+ - port: 5000
254
+ ---
255
+ # Bridge
256
+ apiVersion: apps/v1
257
+ kind: Deployment
258
+ metadata:
259
+ name: bridge
260
+ namespace: tribe-system
261
+ spec:
262
+ replicas: 1
263
+ selector:
264
+ matchLabels:
265
+ app: bridge
266
+ template:
267
+ metadata:
268
+ labels:
269
+ app: bridge
270
+ spec:
271
+ serviceAccountName: bridge
272
+ initContainers:
273
+ - name: wait-for-services
274
+ image: busybox:1.35
275
+ command: ['sh', '-c']
276
+ args:
277
+ - |
278
+ echo "Waiting for services..."
279
+ until nc -z taskmaster 5000; do echo waiting for taskmaster; sleep 2; done
280
+ until nc -z gitea 3000; do echo waiting for gitea; sleep 2; done
281
+ echo "All services ready!"
282
+ containers:
283
+ - name: bridge
284
+ image: tribexal/tribe:latest-bridge
285
+ imagePullPolicy: IfNotPresent
286
+ ports:
287
+ - containerPort: 8080
288
+ - containerPort: 3456
289
+ env:
290
+ - name: TASKMASTER_URL
291
+ value: http://taskmaster:5000
292
+ - name: GITEA_URL
293
+ value: http://gitea:3000
294
+ - name: GITEA_ADMIN_USER
295
+ value: gitea_admin
296
+ - name: GITEA_ADMIN_PASSWORD
297
+ value: admin123
298
+ - name: NAMESPACE
299
+ value: tribe-system
300
+ readinessProbe:
301
+ httpGet:
302
+ path: /health
303
+ port: 8080
304
+ initialDelaySeconds: 10
305
+ periodSeconds: 5
306
+ ---
307
+ apiVersion: v1
308
+ kind: Service
309
+ metadata:
310
+ name: bridge
311
+ namespace: tribe-system
312
+ spec:
313
+ selector:
314
+ app: bridge
315
+ ports:
316
+ - name: http
317
+ port: 8080
318
+ - name: websocket
319
+ port: 3456
320
+ ---
321
+ # Claude Worker Deployment (starts with 0 replicas)
322
+ apiVersion: apps/v1
323
+ kind: Deployment
324
+ metadata:
325
+ name: claude-worker-deployment
326
+ namespace: tribe-system
327
+ spec:
328
+ replicas: 0
329
+ selector:
330
+ matchLabels:
331
+ app: claude-worker
332
+ template:
333
+ metadata:
334
+ labels:
335
+ app: claude-worker
336
+ spec:
337
+ containers:
338
+ - name: claude-agent
339
+ image: tribexal/tribe:latest-claude-agent
340
+ imagePullPolicy: IfNotPresent
341
+ env:
342
+ - name: ROLE
343
+ value: worker
344
+ - name: TASKMASTER_URL
345
+ value: http://taskmaster:5000
346
+ - name: GITEA_URL
347
+ value: http://gitea:3000
348
+ - name: NAMESPACE
349
+ value: tribe-system
350
+ - name: ANTHROPIC_API_KEY
351
+ valueFrom:
352
+ secretKeyRef:
353
+ name: claude-api-key
354
+ key: api-key
355
+ optional: true
356
+ volumeMounts:
357
+ - name: workspace
358
+ mountPath: /workspace
359
+ - name: config
360
+ mountPath: /app/minimal-config
361
+ readinessProbe:
362
+ exec:
363
+ command:
364
+ - /bin/sh
365
+ - -c
366
+ - test -f /tmp/worker-ready
367
+ initialDelaySeconds: 30
368
+ periodSeconds: 10
369
+ volumes:
370
+ - name: workspace
371
+ emptyDir: {}
372
+ - name: config
373
+ configMap:
374
+ name: claude-config
375
+ optional: true
376
+ ---
377
+ # NodePort service for easy access
378
+ apiVersion: v1
379
+ kind: Service
380
+ metadata:
381
+ name: bridge-nodeport
382
+ namespace: tribe-system
383
+ spec:
384
+ type: NodePort
385
+ selector:
386
+ app: bridge
387
+ ports:
388
+ - name: http
389
+ port: 8080
390
+ nodePort: 30080
391
+ - name: websocket
392
+ port: 3456
393
+ nodePort: 30456
394
+ ---
395
+ # ConfigMap for Claude agent configuration
396
+ apiVersion: v1
397
+ kind: ConfigMap
398
+ metadata:
399
+ name: claude-config
400
+ namespace: tribe-system
401
+ data:
402
+ config.yaml: |
403
+ mcp_servers:
404
+ filesystem:
405
+ command: npx
406
+ args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
407
+ git:
408
+ command: npx
409
+ args: ["-y", "@modelcontextprotocol/server-git"]
410
+ env:
411
+ PATH: /usr/local/bin:/usr/bin:/bin
412
+ ---
413
+ # Initialization Job
414
+ apiVersion: batch/v1
415
+ kind: Job
416
+ metadata:
417
+ name: tribe-init
418
+ namespace: tribe-system
419
+ spec:
420
+ template:
421
+ spec:
422
+ restartPolicy: OnFailure
423
+ containers:
424
+ - name: init
425
+ image: curlimages/curl:8.4.0
426
+ command: ['/bin/sh', '-c']
427
+ args:
428
+ - |
429
+ echo "Waiting for Gitea to be ready..."
430
+ until curl -s http://gitea:3000 > /dev/null; do
431
+ echo "Waiting for Gitea..."
432
+ sleep 5
433
+ done
434
+
435
+ echo "Creating Gitea access token..."
436
+ TOKEN=$(curl -s -X POST http://gitea:3000/api/v1/users/gitea_admin/tokens \
437
+ -u gitea_admin:admin123 \
438
+ -H "Content-Type: application/json" \
439
+ -d '{"name":"taskmaster-'$(date +%s)'","scopes":["write:repository","write:user","write:issue","write:organization","read:repository"]}' \
440
+ | grep -o '"sha1":"[^"]*' | cut -d'"' -f4)
441
+
442
+ if [ -z "$TOKEN" ]; then
443
+ echo "Failed to create token!"
444
+ exit 1
445
+ fi
446
+
447
+ echo "Token created successfully"
448
+
449
+ # Update TaskMaster with the token
450
+ echo "Updating TaskMaster configuration..."
451
+ curl -X POST http://taskmaster:5000/api/config \
452
+ -H "Content-Type: application/json" \
453
+ -d "{\"gitea_token\":\"$TOKEN\"}"
454
+
455
+ echo "Initialization complete!"