@inneranimalmedia/agentsam-sdk 1.5.0 → 1.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Agent Sam is a full-stack AI agent SDK for autonomous task execution — covering data management, creative workflows, design commands, and multi-step agentic pipelines.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "files": [
15
15
  "src",
16
+ "templates",
16
17
  "docs",
17
18
  "examples",
18
19
  "test",
package/src/cli.js CHANGED
@@ -4,6 +4,7 @@ import pkg from '../package.json' with { type: 'json' };
4
4
  import readline from 'readline';
5
5
  import { buildLocalScaffoldMeta, LANE_KEYS, RUN_TARGETS } from './lib/local-scaffold.js';
6
6
  import { writeScaffoldFiles } from './lib/write-files.js';
7
+ import { copyGorillaTemplate } from './lib/gorilla-template.js';
7
8
  import { printContextSummary } from './lib/detect-context.js';
8
9
  import { promptOptionalByokKeys } from './lib/prompt-byok.js';
9
10
  import { runStartLocal } from './commands/start-local.js';
@@ -89,9 +90,11 @@ async function runLocalInit(config) {
89
90
  `);
90
91
 
91
92
  const dir = writeScaffoldFiles(meta.projectName, meta.files);
93
+ copyGorillaTemplate(dir, meta);
92
94
 
93
95
  console.log(`
94
96
  ✓ Project ready: ${dir}
97
+ ✓ Gorilla Mode UI → gorilla/ (http://localhost:5173 after npm run dev)
95
98
 
96
99
  Next steps:`);
97
100
  for (const step of meta.next_steps) {
@@ -105,9 +108,8 @@ async function runLocalInit(config) {
105
108
 
106
109
  console.log(`
107
110
  Local in ~60 seconds:
108
- cd ${meta.projectName} && npm install && npm run smoke
109
- npx agentsam start-local
110
- npm run dev
111
+ cd ${meta.projectName} && npm install && npm run smoke && npm run dev
112
+ open http://localhost:5173
111
113
  `);
112
114
  }
113
115
 
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copy Gorilla Mode pixel UI into scaffolded projects (fully local).
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const TEMPLATE_FILES = ['App.tsx', 'main.jsx', 'index.html'];
9
+ const ROOT_FILES = ['vite.config.js'];
10
+
11
+ export function resolveGorillaTemplateDir() {
12
+ const here = path.dirname(fileURLToPath(import.meta.url));
13
+ return path.resolve(here, '..', '..', 'templates', 'gorilla-shell');
14
+ }
15
+
16
+ /**
17
+ * @param {string} projectRoot
18
+ * @param {{ projectName: string, laneKey: string, agent: string, laneLabel: string }} meta
19
+ */
20
+ export function copyGorillaTemplate(projectRoot, meta) {
21
+ const srcDir = resolveGorillaTemplateDir();
22
+ if (!fs.existsSync(srcDir)) {
23
+ throw new Error(`Gorilla template not found at ${srcDir}`);
24
+ }
25
+
26
+ const destDir = path.join(path.resolve(projectRoot), 'gorilla');
27
+ fs.mkdirSync(destDir, { recursive: true });
28
+
29
+ const vars = {
30
+ '{{PROJECT_NAME}}': meta.projectName,
31
+ '{{LANE_KEY}}': meta.laneKey,
32
+ '{{LANE_LABEL}}': meta.laneLabel,
33
+ '{{AGENT}}': meta.agent,
34
+ };
35
+
36
+ for (const name of TEMPLATE_FILES) {
37
+ const src = path.join(srcDir, name);
38
+ if (!fs.existsSync(src)) continue;
39
+ let content = fs.readFileSync(src, 'utf8');
40
+ for (const [token, value] of Object.entries(vars)) {
41
+ content = content.split(token).join(value);
42
+ }
43
+ fs.writeFileSync(path.join(destDir, name), content, 'utf8');
44
+ }
45
+
46
+ const root = path.resolve(projectRoot);
47
+ for (const name of ROOT_FILES) {
48
+ const src = path.join(srcDir, name);
49
+ if (!fs.existsSync(src)) continue;
50
+ fs.copyFileSync(src, path.join(root, name));
51
+ }
52
+
53
+ return destDir;
54
+ }
@@ -125,7 +125,7 @@ export function buildLocalScaffoldFiles({
125
125
  laneLabel,
126
126
  agent,
127
127
  runTarget,
128
- sdkVersion = '1.5.0',
128
+ sdkVersion = '1.5.1',
129
129
  }) {
130
130
  const sdkRange = `^${sdkVersion.split('.').slice(0, 2).join('.')}.0`;
131
131
  const migration = migrationSql(laneKey);
@@ -153,6 +153,8 @@ export function buildLocalScaffoldFiles({
153
153
  deploy_target: runTarget === 'local' ? null : runTarget,
154
154
  pty_port: 3099,
155
155
  dev_port: 8787,
156
+ ui_port: 5173,
157
+ ui: 'gorilla',
156
158
  scaffold_version: sdkVersion,
157
159
  },
158
160
  null,
@@ -169,11 +171,13 @@ Agent Sam runs a PTY on **your machine** — no accounts, no cloudflared, no IAM
169
171
  # Terminal 1 — local PTY (Agent Sam shell bridge)
170
172
  npx agentsam start-local
171
173
 
172
- # Terminal 2 — app dev server
174
+ # Terminal 2 — Gorilla Mode UI + local Worker API
173
175
  npm run dev
174
176
  npm run db:migrate # first time only
175
177
  \`\`\`
176
178
 
179
+ Open **http://localhost:5173** — pixel Gorilla shell proxies \`/api\` → Worker on :8787.
180
+
177
181
  PTY listens on \`ws://127.0.0.1:3099\`. Health: \`curl http://127.0.0.1:3099/health\`
178
182
 
179
183
  When you're ready to ship to Cloudflare or GCP:
@@ -202,7 +206,9 @@ dist/
202
206
  type: 'module',
203
207
  private: true,
204
208
  scripts: {
205
- dev: 'wrangler dev --local',
209
+ dev: 'concurrently -k "npm run dev:worker" "npm run dev:ui"',
210
+ 'dev:worker': 'wrangler dev --local --port 8787',
211
+ 'dev:ui': 'vite',
206
212
  'dev:node': 'node --watch src/dev-server.js',
207
213
  deploy: 'wrangler deploy',
208
214
  smoke: 'node ./scripts/smoke.mjs',
@@ -211,15 +217,27 @@ dist/
211
217
  },
212
218
  dependencies: {
213
219
  '@inneranimalmedia/agentsam-sdk': sdkRange,
220
+ react: '^19.0.0',
221
+ 'react-dom': '^19.0.0',
214
222
  },
215
223
  devDependencies: {
216
224
  wrangler: '^4.0.0',
225
+ vite: '^6.0.0',
226
+ '@vitejs/plugin-react': '^4.0.0',
227
+ concurrently: '^9.0.0',
217
228
  },
218
229
  },
219
230
  null,
220
231
  2,
221
232
  )}\n`,
222
233
  },
234
+ {
235
+ path: '.env',
236
+ content: `VITE_PROJECT_NAME=${projectName}
237
+ VITE_LANE_KEY=${laneKey}
238
+ VITE_AGENT=${agent}
239
+ `,
240
+ },
223
241
  {
224
242
  path: 'wrangler.toml',
225
243
  content: `name = "${projectName}"
@@ -317,13 +335,18 @@ console.log('AgentSam smoke test passed:', data);
317
335
 
318
336
  Built locally with [Agent Sam SDK](https://inneranimalmedia.com) — **${laneLabel}** lane, \`${agent}\` agent.
319
337
 
320
- ## Local-first (default)
338
+ ## Gorilla Mode (default UI)
321
339
 
322
340
  \`\`\`bash
323
341
  npm install
324
342
  npm run smoke
325
- npx agentsam start-local # Terminal 1 local PTY
326
- npm run dev # Terminal 2 — http://127.0.0.1:8787
343
+ npm run dev # Worker :8787 + Vite :5173
344
+ \`\`\`
345
+
346
+ Open **http://localhost:5173** — pixel Gorilla shell. Live \`/health\`, \`/samiam\`, and demo scenarios proxy to your local Worker.
347
+
348
+ \`\`\`bash
349
+ npx agentsam start-local # optional — local PTY on :3099
327
350
  npm run db:migrate # apply local D1 schema
328
351
  \`\`\`
329
352
 
@@ -343,7 +366,7 @@ Run target selected at init: **${runTarget}**
343
366
  ];
344
367
  }
345
368
 
346
- export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.0') {
369
+ export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.1') {
347
370
  const projectName = String(body.projectName || body.project_name || 'agentsam-project')
348
371
  .trim()
349
372
  .toLowerCase()
@@ -364,9 +387,10 @@ export function buildLocalScaffoldMeta(body, sdkVersion = '1.5.0') {
364
387
  next_steps: [
365
388
  'npm install',
366
389
  'npm run smoke',
367
- 'npx agentsam start-local',
368
390
  'npm run dev',
391
+ 'Open http://localhost:5173 — Gorilla Mode UI',
369
392
  'npm run db:migrate',
393
+ 'Optional: npx agentsam start-local',
370
394
  'When ready: npx agentsam deploy',
371
395
  ],
372
396
  };
@@ -0,0 +1,639 @@
1
+ import { useState, useEffect, useRef, useMemo, useCallback } from "react";
2
+
3
+ const PS = 5;
4
+ const PAL = ['transparent','#060e1a','#0f2040','#1a3568','#6a6a90','#9898b8','#2a1006','#8a5830','#ff1a1a','#ffd88a','#0e0400','#ffffff','#ffd700','#ffaa00','#ff6600','#ff2200','#9B6E14','#6B4808','#3a2604','#c09030','#ffee44','#cc8800','#44ff88','#ff4444'];
5
+
6
+ const GORILLA = [
7
+ [0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0],
8
+ [0,0,2,3,3,3,3,3,3,3,3,3,3,2,2,0,0,0],
9
+ [0,2,2,3,3,3,3,3,3,3,3,3,3,3,2,2,0,0],
10
+ [0,2,3,3,7,7,3,3,3,7,7,3,3,3,2,0,0,0],
11
+ [2,2,3,7,7,8,7,3,3,7,8,7,7,3,3,2,2,0],
12
+ [2,2,3,7,7,7,7,7,7,7,7,7,7,3,3,2,2,0],
13
+ [2,3,3,7,6,10,6,7,7,6,10,6,7,3,3,3,2,0],
14
+ [2,3,3,7,7,7,9,9,9,9,7,7,7,3,3,3,2,0],
15
+ [0,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,0],
16
+ [0,2,2,3,4,4,4,4,4,4,4,4,4,3,2,2,0,0],
17
+ [2,2,2,3,4,5,5,5,5,5,5,5,4,3,3,2,2,2],
18
+ [2,3,2,3,4,5,5,5,5,5,5,5,4,3,2,3,2,2],
19
+ [2,3,3,3,3,4,4,5,5,4,4,3,3,3,3,3,2,0],
20
+ [0,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,0,0],
21
+ [0,0,2,3,3,3,3,3,3,3,3,3,3,2,2,0,0,0],
22
+ [0,0,2,2,3,3,3,3,3,3,3,3,2,2,0,0,0,0],
23
+ [0,0,0,2,2,3,3,0,0,3,3,2,2,0,0,0,0,0],
24
+ [0,0,0,2,2,2,2,0,0,2,2,2,2,0,0,0,0,0],
25
+ [0,0,0,0,2,2,2,0,0,2,2,2,0,0,0,0,0,0],
26
+ [0,0,0,0,2,3,2,0,0,2,3,2,0,0,0,0,0,0],
27
+ ];
28
+ const GORILLA_PUMP = GORILLA.map((r,i) => {
29
+ if (i===8) return [2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,0];
30
+ if (i===9) return [2,2,3,4,5,5,5,5,5,5,5,5,5,4,3,2,2,0];
31
+ if (i===10) return [0,2,3,4,5,5,5,5,5,5,5,5,5,4,3,2,0,0];
32
+ if (i===11) return [0,2,3,3,4,5,5,5,5,5,5,5,4,3,3,2,0,0];
33
+ return r;
34
+ });
35
+
36
+ const COIN = [[0,12,12,12,12,12,0],[12,20,20,20,20,20,12],[12,20,12,20,12,20,12],[12,20,20,20,20,20,12],[0,12,12,12,12,12,0],[0,0,21,21,0,0,0]];
37
+ const FLAME_FRAMES = [
38
+ [[0,0,14,0,0],[0,14,15,14,0],[14,15,13,14,0],[14,13,20,14,0],[0,14,13,14,0],[0,13,14,13,0]],
39
+ [[0,14,0,14,0],[14,15,14,0,0],[14,13,15,14,0],[0,14,13,14,0],[0,13,14,0,0],[0,14,13,0,0]],
40
+ [[0,14,14,0,0],[0,14,15,14,0],[14,15,13,15,0],[14,13,14,13,0],[0,14,13,14,0],[0,0,14,13,0]],
41
+ ];
42
+
43
+ const THEMES = {
44
+ NIGHT: { bg:'#030a14', panel:'rgba(4,12,26,.97)', accent:'#00e5ff', dim:'#00e5ff22', glow:'0 0 14px #00e5ff55', border:'#00e5ff33' },
45
+ DAY: { bg:'#071a0a', panel:'rgba(6,20,10,.97)', accent:'#44ff88', dim:'#44ff8822', glow:'0 0 14px #44ff8855', border:'#44ff8833' },
46
+ LAVA: { bg:'#0e0003', panel:'rgba(18,2,4,.97)', accent:'#ff4400', dim:'#ff440022', glow:'0 0 14px #ff440055', border:'#ff440033' },
47
+ VOID: { bg:'#06001a', panel:'rgba(8,2,28,.97)', accent:'#aa44ff', dim:'#aa44ff22', glow:'0 0 14px #aa44ff55', border:'#aa44ff33' },
48
+ };
49
+ const TKEYS = Object.keys(THEMES);
50
+
51
+ // ── Line colors ───────────────────────────────────────────────────────────────
52
+ const LC = {
53
+ cmd: '#00e5ff',
54
+ success: '#44ff88',
55
+ error: '#ff4444',
56
+ warn: '#ffcc00',
57
+ info: '#88bbff',
58
+ muted: 'rgba(255,255,255,.45)',
59
+ dim: 'rgba(255,255,255,.18)',
60
+ white: 'rgba(255,255,255,.9)',
61
+ sam: '#cc88ff',
62
+ table: '#ffdd88',
63
+ pass: '#44ff88',
64
+ fail: '#ff4444',
65
+ accent: '#00e5ff',
66
+ };
67
+
68
+ // ── Scenario Builders ─────────────────────────────────────────────────────────
69
+ const BENCH_TESTS = [
70
+ ['health_check',13],['auth_superadmin',8],['auth_regular',9],['d1_query_basic',45],
71
+ ['d1_query_join',67],['d1_write_record',38],['mcp_tool_routing',23],['mcp_auth_bearer',11],
72
+ ['mcp_audit_log',19],['agent_context_load',88],['r2_upload_asset',54],['r2_fetch_asset',31],
73
+ ['kv_read_hit',7],['kv_write_ttl',14],['queue_publish',28],['worker_ai_t0',112],
74
+ ['gemini_flash_lite',189],['haiku_route',145],['sonnet_route',234],['budget_check',6],
75
+ ['cicd_sandbox_gate',17],['cicd_benchmark_gate',22],['deploy_record_insert',33],
76
+ ['workspace_switch',12],['terminal_session_resume',19],['rag_cosine_query',78],
77
+ ['tool_intent_match',15],['client_workflow_run',44],['project_context_load',29],
78
+ ['skill_lookup',11],['cicd_pipeline_e2e',156],
79
+ ];
80
+
81
+ function buildDeploy() {
82
+ let d = 0; const L = (text, color, gap=120) => { d+=gap; return {text,color,delay:d}; };
83
+ return [
84
+ L('gorilla@inneranimal ~ % /deploy sandbox','cmd',0),
85
+ L('','dim',60),
86
+ L(' ./scripts/with-cloudflare-env.sh npx wrangler deploy','muted',80),
87
+ L(' –config wrangler.sandbox.toml','muted',40),
88
+ L('','dim',80),
89
+ L(' Bundling worker…','muted',300),
90
+ L(' esbuild: scanning 847 modules…','muted',500),
91
+ L(' Tree-shaking complete: 284kb','muted',400),
92
+ L(' Build time: 1.24s','muted',200),
93
+ L('','dim',80),
94
+ L(' Uploading script…','muted',350),
95
+ L('','dim',60),
96
+ L(' Worker inneranimal-dashboard','info',200),
97
+ L(' Compat date 2026-04-08','info',60),
98
+ L(' D1 binding inneranimalmedia-business [cf87b717]','info',60),
99
+ L(' R2 binding agent-sam-sandbox-cicd','info',60),
100
+ L(' KV binding IAM_KV, RATE_LIMIT_KV','info',60),
101
+ L(' Queues DEPLOY_QUEUE, AGENT_QUEUE','info',60),
102
+ L('','dim',80),
103
+ L(' Routing table:','muted',200),
104
+ L(' dashboard.inneranimalmedia.com /* -> inneranimal-dashboard','dim',60),
105
+ L('','dim',80),
106
+ L(' [OK] Deployed successfully 3.41s','success',400),
107
+ L(' >> https://inneranimal-dashboard.samprimeaux.workers.dev','accent',80),
108
+ L('','dim',100),
109
+ L(' Next: run /benchmark to proceed to prod','warn',200),
110
+ ];
111
+ }
112
+
113
+ function buildBenchmark() {
114
+ let d = 0; const L = (text, color, gap=80) => { d+=gap; return {text,color,delay:d}; };
115
+ const lines = [
116
+ L('gorilla@inneranimal ~ % /benchmark','cmd',0),
117
+ L('','dim',60),
118
+ L(' Running 31 tests against inneranimal-dashboard…','muted',200),
119
+ L('','dim',60),
120
+ ];
121
+ BENCH_TESTS.forEach(([name, ms], i) => {
122
+ const num = String(i+1).padStart(2,'0');
123
+ const dots = '.'.repeat(Math.max(2, 36 - name.length));
124
+ lines.push(L(` [${num}/31] ${name} ${dots} PASS ${ms}ms`,'pass',75));
125
+ });
126
+ lines.push(L('','dim',100));
127
+ lines.push(L(' '+'-'.repeat(52),'dim',100));
128
+ lines.push(L(' RESULTS 31 / 31 PASSED avg 47ms total 1.46s','success',200));
129
+ lines.push(L(' '+'-'.repeat(52),'dim',60));
130
+ lines.push(L('','dim',80));
131
+ lines.push(L(' [GATE PASSED] Cleared for production promote','success',300));
132
+ return lines;
133
+ }
134
+
135
+ function buildD1() {
136
+ let d = 0; const L = (text, color, gap=100) => { d+=gap; return {text,color,delay:d}; };
137
+ const SEP = ' +—————————+———+———————+';
138
+ const rows = [
139
+ ['inneranimalmedia','success','2026-04-07 23:10:56'],
140
+ ['inneranimal-dashboard','success','2026-04-07 22:55:15'],
141
+ ['inneranimal-dashboard','success','2026-04-07 22:06:28'],
142
+ ['inneranimalmedia','success','2026-04-06 18:32:11'],
143
+ ['inneranimal-dashboard','error','2026-04-06 17:44:03'],
144
+ ];
145
+ return [
146
+ L('gorilla@inneranimal ~ % /d1 SELECT name, status, created_at FROM deployments ORDER BY created_at DESC LIMIT 5','cmd',0),
147
+ L('','dim',60),
148
+ L(' DB: inneranimalmedia-business','muted',150),
149
+ L(' ID: cf87b717-d4e2-4cf8-bab0-a81268e32d49','dim',60),
150
+ L('','dim',100),
151
+ L(SEP,'table',200),
152
+ L(' | name | status | created_at |','table',60),
153
+ L(SEP,'table',60),
154
+ ...rows.map(([n,s,t]) => L(
155
+ ` | ${n.padEnd(25)} | ${s.padEnd(7)} | ${t} |`,
156
+ s==='error'?'error':'white', 90
157
+ )),
158
+ L(SEP,'table',90),
159
+ L(' 5 rows | 2ms','dim',100),
160
+ ];
161
+ }
162
+
163
+ function buildTail() {
164
+ let d = 0; const L = (text, color, gap=280) => { d+=gap; return {text,color,delay:d}; };
165
+ const reqs = [
166
+ ['GET ','/ ','200','14ms',''],
167
+ ['POST','/api/agent/chat ','200','234ms',''],
168
+ ['GET ','/api/workspace/status ','200','8ms',''],
169
+ ['POST','/mcp ','200','45ms','[MCP]'],
170
+ ['GET ','/api/deployments ','200','12ms',''],
171
+ ['POST','/api/agent/chat ','200','891ms','[LLM]'],
172
+ ['GET ','/health ','200','2ms',''],
173
+ ['POST','/api/d1/query ','200','34ms',''],
174
+ ['GET ','/api/workspace/list ','200','18ms',''],
175
+ ['POST','/api/agent/chat ','500','12ms','[ERR]'],
176
+ ];
177
+ const delays = [0,320,180,260,400,350,220,310,280,450];
178
+ return [
179
+ L('gorilla@inneranimal ~ % /tail inneranimalmedia','cmd',0),
180
+ L('','dim',60),
181
+ L(' npx wrangler tail inneranimalmedia –format pretty','muted',100),
182
+ L(' Streaming live logs… (CTRL+C to stop)','dim',300),
183
+ L('','dim',100),
184
+ ...reqs.map(([m,p,s,t,tag],i) => L(
185
+ ` [10:23:${41+i}] ${m} ${p.padEnd(30)}${s} ${t.padEnd(8)}${tag}`,
186
+ s==='500'?'error': tag==='[LLM]'?'warn': tag==='[MCP]'?'info': 'muted',
187
+ delays[i] || 300
188
+ )),
189
+ L('','dim',200),
190
+ L(' 1 error detected – POST /api/agent/chat returned 500','error',200),
191
+ L(' Tip: run /samiam to diagnose','warn',200),
192
+ ];
193
+ }
194
+
195
+ function buildSamIAm() {
196
+ let d = 0; const L = (text, color, gap=80) => { d+=gap; return {text,color,delay:d}; };
197
+ return [
198
+ L('gorilla@inneranimal ~ % /samiam how is our terminal setup','cmd',0),
199
+ L('','dim',60),
200
+ L(' >> AGENT SAM ACTIVATED','sam',200),
201
+ L(' >> Workspace: ws_inneranimalmedia','sam',100),
202
+ L(' >> PTY context: injecting last 30 lines','sam',100),
203
+ L(' >> Routing: claude-sonnet-4-6 (T2)','sam',100),
204
+ L('','dim',200),
205
+ L(' [SAM] Terminal PTY is running clean. Your xterm.js','sam',400),
206
+ L(' bridge is connected to iam-pty via WebSocket.','sam',150),
207
+ L(' gorilla-mode shell is rendering on top of it.','sam',150),
208
+ L('','dim',80),
209
+ L(' [SAM] One issue flagging: 50 open errors in status','sam',300),
210
+ L(' bar – traced to mcp_services table having 30','sam',150),
211
+ L(' rows all pointing to same endpoint, NULL last_used.','sam',150),
212
+ L(' Table is never read by Worker – safe to truncate.','sam',150),
213
+ L('','dim',80),
214
+ L(' [SAM] Also: terminal_sessions user_id backfill is','sam',300),
215
+ L(' still needed in register INSERT (hardcoded sam).','sam',150),
216
+ L('','dim',80),
217
+ L(' [1] Show me the mcp_services cleanup query','white',300),
218
+ L(' [2] Run /d1 truncate mcp_services directly','white',80),
219
+ L(' [3] Open the terminal_sessions bug in Cursor','white',80),
220
+ L(' [esc] Skip for now','dim',80),
221
+ ];
222
+ }
223
+
224
+ function buildWrangler() {
225
+ let d = 0; const L = (text, color, gap=100) => { d+=gap; return {text,color,delay:d}; };
226
+ return [
227
+ L('gorilla@inneranimal ~ % /wrangler tail inneranimalmedia','cmd',0),
228
+ L('','dim',60),
229
+ L(' [GATE] About to run:','warn',150),
230
+ L(' ./scripts/with-cloudflare-env.sh npx wrangler tail inneranimalmedia','white',80),
231
+ L('','dim',80),
232
+ L(' PROCEED? [1] YES [esc] NO','warn',200),
233
+ L('','dim',100),
234
+ L(' >> 1','cmd',600),
235
+ L('','dim',60),
236
+ L(' Connecting to worker: inneranimalmedia','muted',200),
237
+ L(' Account: Inner Animal Media (6f2a…)','muted',80),
238
+ L(' Zone: inneranimalmedia.com','muted',80),
239
+ L('','dim',100),
240
+ L(' [STREAM] Listening for events…','success',300),
241
+ L('','dim',80),
242
+ L(' 10:31:04 GET / 200 11ms','dim',400),
243
+ L(' 10:31:06 POST /api/agent/chat 200 445ms','dim',350),
244
+ L(' 10:31:07 GET /api/status 200 6ms','dim',250),
245
+ L(' 10:31:09 POST /mcp 200 88ms [MCP]','info',380),
246
+ L(' 10:31:11 POST /api/agent/chat 200 1201ms [LLM]','warn',620),
247
+ L('','dim',200),
248
+ L(' LLM call latency spike: 1201ms – check Sonnet routing','warn',200),
249
+ ];
250
+ }
251
+
252
+ const SCENARIOS = [
253
+ { key:'deploy', label:'/deploy', fn:buildDeploy, status:'DEPLOYING', coins:6, pump:true, errAt:null },
254
+ { key:'benchmark', label:'/benchmark', fn:buildBenchmark, status:'BENCHMARKING',coins:12, pump:true, errAt:null },
255
+ { key:'d1', label:'/d1 query', fn:buildD1, status:'QUERYING D1',coins:3, pump:false, errAt:null },
256
+ { key:'tail', label:'/tail', fn:buildTail, status:'TAILING', coins:0, pump:false, errAt:9 },
257
+ { key:'samiam', label:'/samiam', fn:buildSamIAm, status:'SAM ACTIVE', coins:4, pump:true, errAt:null },
258
+ { key:'wrangler', label:'/wrangler', fn:buildWrangler, status:'CF GATE', coins:2, pump:false, errAt:null },
259
+ ];
260
+
261
+ // ── Sprite Renderer ───────────────────────────────────────────────────────────
262
+ function Sprite({ data, scale=1 }) {
263
+ const ps = PS * scale;
264
+ return (
265
+ <svg width={data[0].length*ps} height={data.length*ps} style={{imageRendering:'pixelated',display:'block'}}>
266
+ {data.flatMap((row,y) => row.map((c,x) =>
267
+ c ? <rect key={`${x},${y}`} x={x*ps} y={y*ps} width={ps} height={ps} fill={PAL[c]}/> : null
268
+ ))}
269
+ </svg>
270
+ );
271
+ }
272
+
273
+ // ── Bar ───────────────────────────────────────────────────────────────────────
274
+ function Bar({ pct, color, label, val }) {
275
+ return (
276
+ <div style={{marginBottom:10}}>
277
+ <div style={{display:'flex',justifyContent:'space-between',fontSize:9,letterSpacing:2,color:'rgba(255,255,255,.4)',marginBottom:3}}>
278
+ <span>{label}</span><span style={{color}}>{val}</span>
279
+ </div>
280
+ <div style={{height:6,background:'rgba(255,255,255,.07)',position:'relative'}}>
281
+ <div style={{position:'absolute',top:0,left:0,height:'100%',width:`${pct}%`,background:color,transition:'width .4s ease',boxShadow:`0 0 6px ${color}`}}/>
282
+ </div>
283
+ </div>
284
+ );
285
+ }
286
+
287
+ // ── Main ──────────────────────────────────────────────────────────────────────
288
+ const PROJECT = import.meta.env.VITE_PROJECT_NAME || '{{PROJECT_NAME}}';
289
+ const LANE = import.meta.env.VITE_LANE_KEY || '{{LANE_KEY}}';
290
+ const AGENT = import.meta.env.VITE_AGENT || '{{AGENT}}';
291
+
292
+ export default function GorillaMode() {
293
+ const [themeIdx, setThemeIdx] = useState(0);
294
+ const [lines, setLines] = useState([{text:`gorilla@${PROJECT} ~ % Gorilla Mode ready — type /help or pick a demo.`, color:'dim', delay:0}]);
295
+ const [running, setRunning] = useState(false);
296
+ const [status, setStatus] = useState('READY');
297
+ const [progress, setProgress] = useState(0);
298
+ const [pump, setPump] = useState(false);
299
+ const [errorFlash, setError] = useState(false);
300
+ const [coinCount, setCoinCount] = useState(0);
301
+ const [floatCoins, setFloat] = useState([]);
302
+ const [flameTick, setFlameTick] = useState(0);
303
+ const [booted, setBooted] = useState(false);
304
+ const [bootLine, setBootLine] = useState('');
305
+ const [activeScenario, setActive] = useState(null);
306
+ const [inputLine, setInputLine] = useState('');
307
+ const [apiOnline, setApiOnline] = useState(false);
308
+ const termRef = useRef(null);
309
+ const timerRefs = useRef([]);
310
+ const inputRef = useRef(null);
311
+
312
+ const appendLine = useCallback((text, color = 'white') => {
313
+ setLines((prev) => [...prev, { text, color, delay: 0 }]);
314
+ }, []);
315
+
316
+ const runLiveSam = useCallback(async (message) => {
317
+ const msg = String(message || '').trim();
318
+ if (!msg) return;
319
+ setRunning(true);
320
+ setStatus('SAM ACTIVE');
321
+ setPump(true);
322
+ appendLine(' >> AGENT SAM (local Worker API)', 'sam');
323
+ try {
324
+ const res = await fetch('/api/agentsam/message', {
325
+ method: 'POST',
326
+ headers: { 'Content-Type': 'application/json' },
327
+ body: JSON.stringify({ message: msg, lane: LANE, agent: AGENT }),
328
+ });
329
+ const data = await res.json().catch(() => ({}));
330
+ if (!res.ok || !data.ok) {
331
+ appendLine(` [ERR] ${data.error || res.status}`, 'error');
332
+ } else {
333
+ appendLine(` [intent] ${data.intent} · agent ${data.agent} · lane ${data.lane}`, 'sam');
334
+ (data.next_steps || []).forEach((step) => appendLine(` → ${step}`, 'white'));
335
+ if (data.requires_approval) appendLine(' ⚠ requires approval before destructive action', 'warn');
336
+ }
337
+ } catch (e) {
338
+ appendLine(` [ERR] ${e?.message || 'API unreachable — is npm run dev:worker running?'}`, 'error');
339
+ } finally {
340
+ setRunning(false);
341
+ setPump(false);
342
+ setStatus(apiOnline ? 'READY' : 'API OFFLINE');
343
+ }
344
+ }, [appendLine, apiOnline]);
345
+
346
+ const T = THEMES[TKEYS[themeIdx]];
347
+
348
+ // Boot
349
+ useEffect(() => {
350
+ const BOOT = [`GORILLA MODE v1.5`,`>> PROJECT: ${PROJECT}`,`>> LANE: ${LANE}`,`>> AGENT: ${AGENT}`,`>> PROBING LOCAL API…`];
351
+ let i = 0;
352
+ const t = setInterval(() => { if(i<BOOT.length){setBootLine(BOOT[i]);i++;}else{clearInterval(t);setTimeout(async ()=>{
353
+ try {
354
+ const res = await fetch('/api/health');
355
+ setApiOnline(res.ok);
356
+ if (res.ok) setBootLine('>> AGENT SAM: ONLINE (local)');
357
+ else setBootLine('>> AGENT SAM: waiting for worker…');
358
+ } catch {
359
+ setApiOnline(false);
360
+ setBootLine('>> AGENT SAM: start npm run dev');
361
+ }
362
+ setTimeout(()=>setBooted(true),400);
363
+ },200);}}, 340);
364
+ return () => clearInterval(t);
365
+ }, []);
366
+
367
+ // Flame tick
368
+ useEffect(() => {
369
+ const t = setInterval(() => setFlameTick(n => (n+1)%3), 190);
370
+ return () => clearInterval(t);
371
+ }, []);
372
+
373
+ // Auto-scroll terminal
374
+ useEffect(() => {
375
+ if (termRef.current) termRef.current.scrollTop = termRef.current.scrollHeight;
376
+ }, [lines]);
377
+
378
+ const stars = useMemo(() =>
379
+ Array.from({length:40},(_,i) => ({id:i,x:Math.random()*100,y:Math.random()*45,s:Math.random()<.2?2:1,d:Math.random()*4}))
380
+ ,[]);
381
+
382
+ const spawnCoins = useCallback((n) => {
383
+ if (!n) return;
384
+ const batch = Array.from({length:n},(_,i) => ({id:Date.now()+i,x:15+Math.random()*30,y:40+Math.random()*15,dx:(Math.random()-.5)*50}));
385
+ setFloat(p=>[...p,...batch]);
386
+ setCoinCount(c=>c+n);
387
+ setTimeout(()=>setFloat(p=>p.filter(c=>!batch.find(b=>b.id===c.id))),1400);
388
+ },[]);
389
+
390
+ const runScenario = useCallback((s) => {
391
+ if (running) return;
392
+ timerRefs.current.forEach(clearTimeout);
393
+ timerRefs.current = [];
394
+ const scenario = SCENARIOS.find(x=>x.key===s);
395
+ if (!scenario) return;
396
+ const sceneLines = scenario.fn();
397
+ const maxDelay = Math.max(...sceneLines.map(l=>l.delay));
398
+ setLines([]);
399
+ setRunning(true);
400
+ setStatus(scenario.status);
401
+ setProgress(0);
402
+ setActive(s);
403
+
404
+ sceneLines.forEach((line,i) => {
405
+ const t = setTimeout(() => {
406
+ setLines(p=>[...p,line]);
407
+ setProgress(Math.round(((i+1)/sceneLines.length)*100));
408
+ if (line.color==='error'||line.color==='fail') {
409
+ setError(true); setTimeout(()=>setError(false),600);
410
+ }
411
+ }, line.delay);
412
+ timerRefs.current.push(t);
413
+ });
414
+
415
+ const done = setTimeout(() => {
416
+ setRunning(false);
417
+ setStatus('READY');
418
+ setProgress(100);
419
+ if (scenario.pump) { setPump(true); setTimeout(()=>setPump(false),700); }
420
+ spawnCoins(scenario.coins);
421
+ }, maxDelay + 400);
422
+ timerRefs.current.push(done);
423
+
424
+ }, [running, spawnCoins]);
425
+
426
+ const runCommand = useCallback(async (raw) => {
427
+ const cmd = String(raw || '').trim();
428
+ if (!cmd || running) return;
429
+ appendLine(`gorilla@${PROJECT} ~ % ${cmd}`, 'cmd');
430
+ setInputLine('');
431
+ if (cmd === '/help') {
432
+ appendLine(' /health · /info · /samiam <msg> · demo: /deploy /benchmark /d1 /tail /wrangler', 'info');
433
+ appendLine(' Or type any goal — hits local POST /api/agentsam/message', 'muted');
434
+ return;
435
+ }
436
+ if (cmd === '/health') {
437
+ try {
438
+ const res = await fetch('/api/health');
439
+ appendLine(` ${await res.text()}`, res.ok ? 'success' : 'error');
440
+ setApiOnline(res.ok);
441
+ } catch (e) {
442
+ appendLine(` ${e?.message || 'offline'}`, 'error');
443
+ setApiOnline(false);
444
+ }
445
+ return;
446
+ }
447
+ if (cmd === '/info') {
448
+ try {
449
+ const res = await fetch('/api/agentsam/info');
450
+ appendLine(` ${await res.text()}`, res.ok ? 'info' : 'error');
451
+ } catch (e) {
452
+ appendLine(` ${e?.message || 'offline'}`, 'error');
453
+ }
454
+ return;
455
+ }
456
+ if (cmd.startsWith('/samiam ')) {
457
+ await runLiveSam(cmd.slice(8));
458
+ return;
459
+ }
460
+ const demoKey = cmd.startsWith('/') ? cmd.slice(1).split(/\s+/)[0] : '';
461
+ if (demoKey && SCENARIOS.find((s) => s.key === demoKey)) {
462
+ runScenario(demoKey);
463
+ return;
464
+ }
465
+ if (cmd.startsWith('/')) {
466
+ appendLine(` Unknown command. Try /help`, 'warn');
467
+ return;
468
+ }
469
+ await runLiveSam(cmd);
470
+ }, [appendLine, runLiveSam, running, runScenario]);
471
+
472
+ const CSS = `@keyframes idle {0%,100%{transform:translateY(0)} 50%{transform:translateY(-4px)}} @keyframes pump {0%{transform:scale(1)} 30%{transform:scale(1.12) translateY(-7px)} 70%{transform:scale(.97)} 100%{transform:scale(1)}} @keyframes shake {0%,100%{transform:translateX(0)} 20%{transform:translateX(-4px)} 40%{transform:translateX(4px)} 60%{transform:translateX(-3px)} 80%{transform:translateX(3px)}} @keyframes rise {0%{transform:translateY(0) scale(1);opacity:1} 100%{transform:translateY(-80px) scale(.3);opacity:0}} @keyframes blink {0%,100%{opacity:1} 50%{opacity:0}} @keyframes twinkle {0%,100%{opacity:.7} 50%{opacity:.1}} @keyframes flicker {0%,100%{transform:scaleY(1)} 50%{transform:scaleY(1.12) scaleX(.9)}} @keyframes scan {0%{opacity:.02} 50%{opacity:.05} 100%{opacity:.02}} @keyframes fadein {from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:none}} @keyframes errflash{0%,100%{box-shadow:none} 50%{box-shadow:0 0 30px #ff444488 inset}} .scen-btn{cursor:pointer;transition:all .12s;font-family:"Courier New",monospace;} .scen-btn:hover{transform:translateY(-1px);} .scen-btn:disabled{opacity:.35;cursor:not-allowed;transform:none;}`;
473
+
474
+ if (!booted) return (
475
+ <div style={{width:'100%',height:'100vh',background:'#030a14',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',fontFamily:'"Courier New",monospace',color:'#00e5ff'}}>
476
+ <style>{CSS}</style>
477
+ <div style={{fontSize:10,letterSpacing:6,opacity:.35,marginBottom:14}}>INNERANIMAL MEDIA</div>
478
+ <div style={{fontSize:15,letterSpacing:2,textShadow:'0 0 10px #00e5ff'}}>
479
+ {bootLine}<span style={{animation:'blink .7s infinite'}}>_</span>
480
+ </div>
481
+ </div>
482
+ );
483
+
484
+ return (
485
+ <div style={{width:'100%',minHeight:'100vh',background:`linear-gradient(160deg,${T.bg} 0%,#030a14 100%)`,fontFamily:'"Courier New",monospace',position:'relative',overflow:'hidden'}}>
486
+ <style>{CSS}</style>
487
+
488
+ {/* Scanlines */}
489
+ <div style={{position:'absolute',inset:0,pointerEvents:'none',zIndex:99,
490
+ backgroundImage:`repeating-linear-gradient(0deg,${T.dim.replace('22','06')} 0,${T.dim.replace('22','06')} 1px,transparent 1px,transparent 3px)`,
491
+ animation:'scan 5s infinite'}}/>
492
+
493
+ {/* Stars */}
494
+ {stars.map(s=>(
495
+ <div key={s.id} style={{position:'absolute',left:`${s.x}%`,top:`${s.y}%`,width:s.s,height:s.s,background:'#fff',
496
+ animation:`twinkle ${2+s.d}s infinite`,animationDelay:`${s.d}s`,pointerEvents:'none'}}/>
497
+ ))}
498
+
499
+ {/* Floating coins */}
500
+ {floatCoins.map(c=>(
501
+ <div key={c.id} style={{position:'absolute',left:`${c.x}%`,top:`${c.y}%`,
502
+ animation:'rise 1.3s ease-out forwards',transform:`translateX(${c.dx}px)`,pointerEvents:'none',zIndex:60}}>
503
+ <Sprite data={COIN} scale={.9}/>
504
+ </div>
505
+ ))}
506
+
507
+ {/* Top bar */}
508
+ <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',
509
+ padding:'10px 18px 8px',borderBottom:`1px solid ${T.border}`,background:T.panel}}>
510
+ <div>
511
+ <span style={{fontSize:9,letterSpacing:5,color:T.accent,opacity:.6}}>INNERANIMAL MEDIA // </span>
512
+ <span style={{fontSize:12,fontWeight:900,letterSpacing:4,color:'#fff',textShadow:T.glow}}>GORILLA MODE</span>
513
+ </div>
514
+ <div style={{display:'flex',alignItems:'center',gap:16}}>
515
+ <div style={{display:'flex',alignItems:'center',gap:6,background:'rgba(0,0,0,.5)',border:`1px solid ${T.accent}`,padding:'3px 10px'}}>
516
+ <Sprite data={COIN} scale={.45}/>
517
+ <span style={{color:T.accent,fontSize:11,letterSpacing:2}}>x {coinCount}</span>
518
+ </div>
519
+ <button className="scen-btn" onClick={()=>setThemeIdx(i=>(i+1)%TKEYS.length)}
520
+ style={{padding:'4px 10px',background:'transparent',border:`1px solid ${T.border}`,color:T.accent,fontSize:9,letterSpacing:2}}>
521
+ {TKEYS[themeIdx]}
522
+ </button>
523
+ </div>
524
+ </div>
525
+
526
+ {/* Main split */}
527
+ <div style={{display:'flex',height:'calc(100vh - 130px)',minHeight:400}}>
528
+
529
+ {/* LEFT: Gorilla + HUD */}
530
+ <div style={{width:200,minWidth:180,borderRight:`1px solid ${T.border}`,background:T.panel,
531
+ display:'flex',flexDirection:'column',alignItems:'center',padding:'16px 14px',gap:0,flexShrink:0}}>
532
+
533
+ {/* Gorilla */}
534
+ <div style={{
535
+ animation: errorFlash ? 'shake .5s ease' : pump ? 'pump .6s ease' : 'idle 2.8s ease-in-out infinite',
536
+ filter: errorFlash ? 'brightness(1.5) saturate(2) hue-rotate(-20deg)' : 'none',
537
+ marginBottom:8,
538
+ }}>
539
+ <Sprite data={pump ? GORILLA_PUMP : GORILLA} scale={1.05}/>
540
+ </div>
541
+
542
+ {/* Flames */}
543
+ <div style={{display:'flex',justifyContent:'center',gap:60,marginTop:-4}}>
544
+ {[0,1].map(i=>(
545
+ <div key={i} style={{animation:'flicker .45s infinite',animationDelay:`${i*.2}s`}}>
546
+ <Sprite data={FLAME_FRAMES[flameTick]} scale={.9}/>
547
+ </div>
548
+ ))}
549
+ </div>
550
+
551
+ {/* Status */}
552
+ <div style={{width:'100%',marginTop:14,paddingTop:12,borderTop:`1px solid ${T.border}`}}>
553
+ <div style={{fontSize:9,letterSpacing:3,color:T.accent,marginBottom:10,textAlign:'center',textShadow:T.glow}}>
554
+ {status}
555
+ {running && <span style={{animation:'blink .7s infinite',marginLeft:4}}>_</span>}
556
+ </div>
557
+
558
+ <Bar pct={progress} color={T.accent} label="PROGRESS" val={`${progress}%`}/>
559
+ <Bar pct={Math.max(0,100-progress)} color="#ff4444" label="ERRORS" val="50"/>
560
+ <Bar pct={running?65:100} color="#44ff88" label="AGENT" val={running?'BUSY':'READY'}/>
561
+
562
+ <div style={{marginTop:12,paddingTop:10,borderTop:`1px solid ${T.border}`,fontSize:9,color:'rgba(255,255,255,.3)',letterSpacing:1,lineHeight:1.8}}>
563
+ <div>{PROJECT}</div>
564
+ <div>lane: {LANE} · {AGENT}</div>
565
+ <div>api: {apiOnline ? 'local :8787' : 'offline'}</div>
566
+ <div style={{color:activeScenario?T.accent:'inherit'}}>
567
+ {activeScenario ? `> ${activeScenario}` : '> idle'}
568
+ </div>
569
+ </div>
570
+ </div>
571
+ </div>
572
+
573
+ {/* RIGHT: Terminal */}
574
+ <div style={{flex:1,display:'flex',flexDirection:'column',overflow:'hidden'}}>
575
+
576
+ {/* Terminal header */}
577
+ <div style={{padding:'7px 16px',borderBottom:`1px solid ${T.border}`,background:'rgba(0,0,0,.4)',
578
+ display:'flex',alignItems:'center',gap:8}}>
579
+ <div style={{width:8,height:8,borderRadius:'50%',background:running?T.accent:'#ffffff22',boxShadow:running?T.glow:'none',transition:'all .3s'}}/>
580
+ <span style={{fontSize:9,letterSpacing:2,color:'rgba(255,255,255,.3)'}}>TERMINAL OUTPUT</span>
581
+ <span style={{marginLeft:'auto',fontSize:9,color:'rgba(255,255,255,.2)',letterSpacing:1}}>{lines.length} lines</span>
582
+ </div>
583
+
584
+ {/* Output */}
585
+ <div ref={termRef} style={{flex:1,overflowY:'auto',padding:'14px 18px',
586
+ animation: errorFlash ? 'errflash .5s ease' : 'none',
587
+ scrollbarWidth:'thin',scrollbarColor:`${T.accent}33 transparent`}}>
588
+ {lines.map((line, i) => (
589
+ <div key={i} style={{
590
+ fontFamily:'"Courier New",monospace',fontSize:12,lineHeight:1.65,
591
+ whiteSpace:'pre',color: LC[line.color] || LC.white,
592
+ animation: i===lines.length-1 ? 'fadein .15s ease' : 'none',
593
+ }}>
594
+ {line.text || '\u00A0'}
595
+ </div>
596
+ ))}
597
+ {running && (
598
+ <div style={{color:T.accent,fontSize:12,animation:'blink .7s infinite',marginTop:2}}>_</div>
599
+ )}
600
+ </div>
601
+
602
+ {/* Input line */}
603
+ <div style={{padding:'8px 16px',borderTop:`1px solid ${T.border}`,background:'rgba(0,0,0,.5)',
604
+ display:'flex',alignItems:'center',gap:8}}>
605
+ <span style={{color:T.accent,fontSize:12,whiteSpace:'nowrap'}}>gorilla@{PROJECT} ~ %</span>
606
+ <input
607
+ ref={inputRef}
608
+ value={inputLine}
609
+ disabled={running}
610
+ onChange={(e) => setInputLine(e.target.value)}
611
+ onKeyDown={(e) => { if (e.key === 'Enter') void runCommand(inputLine); }}
612
+ placeholder={running ? 'running…' : '/help · /health · /samiam …'}
613
+ style={{
614
+ flex:1, fontSize:12, letterSpacing:.5, color:'rgba(255,255,255,.85)',
615
+ background:'transparent', border:'none', outline:'none', fontFamily:'"Courier New",monospace',
616
+ }}
617
+ />
618
+ </div>
619
+ </div>
620
+ </div>
621
+
622
+ {/* Scenario buttons */}
623
+ <div style={{borderTop:`1px solid ${T.border}`,background:T.panel,
624
+ display:'flex',flexWrap:'wrap',gap:0}}>
625
+ {SCENARIOS.map(s=>(
626
+ <button key={s.key} className="scen-btn" disabled={running}
627
+ onClick={()=>runScenario(s.key)}
628
+ style={{flex:'1 1 0',padding:'10px 8px',minWidth:100,
629
+ background: activeScenario===s.key ? T.dim : 'transparent',
630
+ border:'none',borderRight:`1px solid ${T.border}`,
631
+ color: activeScenario===s.key ? T.accent : 'rgba(255,255,255,.5)',
632
+ fontSize:11,letterSpacing:1.5,textShadow: activeScenario===s.key ? T.glow : 'none'}}>
633
+ {s.label}
634
+ </button>
635
+ ))}
636
+ </div>
637
+ </div>
638
+ );
639
+ }
@@ -0,0 +1,22 @@
1
+ # Gorilla Shell (Phase 0)
2
+
3
+ Game-feel terminal UI prototype — consolidated from [InnerAnimal/gorilla-mode](https://github.com/InnerAnimal/gorilla-mode) into **Agent Sam SDK**.
4
+
5
+ This is the **unique install experience** layer: pixel HUD, themed moods, slash-command demos, deploy/benchmark scenarios. Phase 1 connects real PTY via ExecOS.
6
+
7
+ ## Run locally
8
+
9
+ ```bash
10
+ cd examples/gorilla-shell
11
+ npm install
12
+ npm run dev
13
+ ```
14
+
15
+ ## What you see
16
+
17
+ - Gorilla launch screen + sprite reactions
18
+ - Themes: NIGHT, DAY, LAVA, VOID
19
+ - Six demo scenarios (deploy, benchmark, D1, tail, samiam, wrangler)
20
+ - Slash command registry lives in `../../src/lib/slash-commands.js`
21
+
22
+ Full architecture: [docs/CLI_SHELL.md](../../docs/CLI_SHELL.md)
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Agent Sam — Gorilla Mode · {{PROJECT_NAME}}</title>
7
+ <style>
8
+ html, body, #root { margin: 0; height: 100%; background: #030a14; }
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ <script type="module" src="/main.jsx"></script>
14
+ </body>
15
+ </html>
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App.tsx';
4
+
5
+ createRoot(document.getElementById('root')).render(
6
+ <React.StrictMode>
7
+ <App />
8
+ </React.StrictMode>,
9
+ );
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@inneranimalmedia/agentsam-shell-example",
3
+ "private": true,
4
+ "type": "module",
5
+ "description": "Phase 0 Gorilla Shell — game-feel CLI UX prototype for Agent Sam SDK",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^19.1.0",
13
+ "react-dom": "^19.1.0"
14
+ },
15
+ "devDependencies": {
16
+ "@vitejs/plugin-react": "^4.5.2",
17
+ "vite": "^6.3.5"
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ root: 'gorilla',
6
+ plugins: [react()],
7
+ server: {
8
+ port: 5173,
9
+ strictPort: true,
10
+ proxy: {
11
+ '/api': {
12
+ target: 'http://127.0.0.1:8787',
13
+ changeOrigin: true,
14
+ },
15
+ },
16
+ },
17
+ build: {
18
+ outDir: '../dist/gorilla',
19
+ emptyOutDir: true,
20
+ },
21
+ });
package/test/smoke.mjs CHANGED
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import { AgentSam, routeIntent, getToolCatalog } from '../src/index.js';
6
6
  import { buildLocalScaffoldMeta } from '../src/lib/local-scaffold.js';
7
7
  import { writeScaffoldFiles } from '../src/lib/write-files.js';
8
+ import { copyGorillaTemplate } from '../src/lib/gorilla-template.js';
8
9
  import { printContextSummary, missingForInit } from '../src/lib/detect-context.js';
9
10
 
10
11
  const app = new AgentSam({ project: 'smoke', lane: 'cms', agent: 'cms' });
@@ -56,6 +57,14 @@ const localMeta = buildLocalScaffoldMeta({ projectName: 'demo', lane: 'cms', run
56
57
  assert.equal(localMeta.laneKey, 'cms');
57
58
  assert.ok(localMeta.files.some((f) => f.path === '.agentsam/start-local.md'));
58
59
  assert.ok(localMeta.files.some((f) => f.path === 'wrangler.toml'));
60
+ assert.ok(localMeta.files.some((f) => f.path === '.env'));
59
61
  assert.ok(!localMeta.files.some((f) => f.path.includes('execos')));
60
62
 
63
+ const gorillaDir = path.join(tmp, 'gorilla-project');
64
+ writeScaffoldFiles(gorillaDir, localMeta.files);
65
+ copyGorillaTemplate(gorillaDir, localMeta);
66
+ assert.ok(fs.existsSync(path.join(gorillaDir, 'gorilla', 'App.tsx')));
67
+ assert.ok(fs.existsSync(path.join(gorillaDir, 'vite.config.js')));
68
+ assert.ok(fs.readFileSync(path.join(gorillaDir, 'gorilla', 'App.tsx'), 'utf8').includes('demo'));
69
+
61
70
  console.log('SDK smoke tests passed');