orrin-cli 0.1.8.2__tar.gz → 0.1.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orrin-cli
3
- Version: 0.1.8.2
3
+ Version: 0.1.9
4
4
  Summary: Orrin CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -26,7 +26,7 @@ app.add_typer(ui, name="ui")
26
26
  projects = typer.Typer(help='OrrinSDK project commands')
27
27
  app.add_typer(projects, name='projects')
28
28
 
29
- API_BASE = "https://stellr-company.com"#"http://192.168.1.153:8080"
29
+ API_BASE = "https://stellr-company.com"
30
30
 
31
31
  APP_NAME = "orrin"
32
32
  APP_AUTHOR = "orrin" # optional but recommended on Windows
@@ -198,6 +198,17 @@ def init_app_backend_project():
198
198
  'name': project_name,
199
199
  'desc': project_desc
200
200
  },
201
+ 'public': {
202
+ 'display_name': project_name,
203
+ 'display_desc': project_desc,
204
+ 'whats_new': '<describe_version>',
205
+ 'authors': [],
206
+ 'open_source': False,
207
+ 'copyright': '<your_copyright>',
208
+ 'dependencies': [
209
+ {}
210
+ ]
211
+ }
201
212
  }
202
213
 
203
214
  path = os.path.join(os.getcwd(), project_name)
@@ -253,32 +264,32 @@ def init_tool_project():
253
264
  )
254
265
 
255
266
  project_toml = {
256
- 'general': {
257
- 'project_type': 'tool',
258
- 'name': project_name,
259
- 'desc': project_desc,
260
- 'nuances': {}
261
- },
267
+ 'general': {
268
+ 'project_type': 'tool',
269
+ 'name': project_name,
270
+ 'desc': project_desc,
271
+ 'nuances': {},
262
272
  'icons': {
263
273
  '24x24': '<path>',
264
274
  '32x32': '<path>',
265
275
  '48x48': '<path>',
266
276
  '64x64': '<path>'
267
277
  },
268
- 'plugins': {
269
- 'supported': False
270
- },
271
- 'metadata': {},
272
- 'public': {
273
- 'display_name': '',
274
- 'display_desc': '',
275
- 'usage_rundown': '',
276
- 'whats_new': '',
277
- 'authors': [],
278
- 'open_source': False,
279
- 'copyright': ''
280
- }
278
+ },
279
+ 'plugins': {
280
+ 'supported': False
281
+ },
282
+ 'metadata': {},
283
+ 'public': {
284
+ 'display_name': project_name,
285
+ 'display_desc': project_desc,
286
+ 'usage_rundown': '<rundown_of_tool_usage>',
287
+ 'whats_new': '<explain_version>',
288
+ 'authors': [],
289
+ 'open_source': False,
290
+ 'copyright': '<your_copyright>'
281
291
  }
292
+ }
282
293
 
283
294
  # Create project
284
295
  path = os.path.join(os.getcwd(), project_name)
@@ -446,8 +457,8 @@ def connect_to_project():
446
457
  def itp():
447
458
  init_tool_project()
448
459
 
449
- """@projects.command(help='Get help creating a tool or app backend')
450
- def orrin_cli():
460
+ """@app.command(help='Launch GUI interface for OrrinCLI and get access to tools and agents')
461
+ def launch():
451
462
  from minimal_display import OrrinCLI
452
463
  OrrinCLI().start()"""
453
464
 
@@ -0,0 +1,548 @@
1
+ from pathlib import Path
2
+ import flet as ft
3
+ from orrinsdk import OrrinAISDK, Models
4
+
5
+ class OrrinCLI:
6
+ # --- DESIGN SYSTEM ---
7
+ BG_BASE = "#09090B" # Deep black/grey (Editor/Main)
8
+ BG_PANEL = "#18181B" # Slightly lighter (Sidebars/Chat)
9
+ BG_SURFACE = "#27272A" # Interactive/Hover elements
10
+ BORDER = "#27272A" # Subtle dividers
11
+ TEXT_PRIMARY = "#FAFAFA" # High contrast text
12
+ TEXT_SECONDARY = "#A1A1AA" # Muted text
13
+ ACCENT = "#3B82F6" # Cursor/Modern Blue
14
+ ACCENT_HOVER = "#2563EB"
15
+
16
+ def __init__(self):
17
+ self.root = Path.cwd()
18
+ self.python_files = list(self.root.rglob("*.py"))
19
+ self.connected_file = None
20
+ self.file_content = ""
21
+
22
+ # ---------------- AI INIT ----------------
23
+ # Kept your exact initialization parameters
24
+ self.ai = OrrinAISDK(
25
+ api_key='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',
26
+ model=Models.GPT_5_MINI
27
+ )
28
+ self.messages = []
29
+
30
+ # ---------------- UI STATE & CONTROLS ----------------
31
+ self.page = None
32
+ self.main_container = ft.Container(expand=True, bgcolor=self.BG_BASE)
33
+
34
+ # Modern Floating Chat Input
35
+ self.chat_input_field = ft.TextField(
36
+ expand=True,
37
+ hint_text="Ask Orrin to edit or explain...",
38
+ hint_style=ft.TextStyle(color=self.TEXT_SECONDARY, size=13, font_family="Inter"),
39
+ text_style=ft.TextStyle(color=self.TEXT_PRIMARY, size=13, font_family="Inter"),
40
+ border=ft.InputBorder.NONE,
41
+ cursor_color=self.ACCENT,
42
+ content_padding=ft.padding.symmetric(vertical=14, horizontal=16),
43
+ on_submit=self.send_message,
44
+ on_change=self.update_send_button_state,
45
+ shift_enter=True,
46
+ multiline=True,
47
+ min_lines=1,
48
+ max_lines=6,
49
+ )
50
+
51
+ self.send_button = ft.IconButton(
52
+ icon=ft.icons.ARROW_UPWARD_ROUNDED,
53
+ icon_color=ft.colors.BLACK26, # Default (will be updated dynamically)
54
+ bgcolor=ft.colors.WHITE,
55
+ icon_size=16,
56
+ width=28,
57
+ height=28,
58
+ style=ft.ButtonStyle(
59
+ padding=0,
60
+ shape=ft.RoundedRectangleBorder(radius=6)
61
+ ),
62
+ tooltip="Send (Enter)",
63
+ on_click=self.send_message,
64
+ # Store reference
65
+ ref=ft.Ref(), # Optional but helpful
66
+ )
67
+
68
+ self.chat_listview = ft.ListView(
69
+ expand=True,
70
+ spacing=24,
71
+ auto_scroll=True,
72
+ padding=ft.padding.only(right=8, top=16, bottom=16)
73
+ )
74
+
75
+ # ---------------- MAIN ENTRY ----------------
76
+ def main(self, page: ft.Page):
77
+ self.page = page
78
+ page.title = "OrrinCLI - AI Code Editor"
79
+ page.bgcolor = self.BG_BASE
80
+ page.window.width = 1200
81
+ page.window.height = 850
82
+ page.padding = 0
83
+
84
+ # Beautiful custom scrollbars and theme settings
85
+ page.theme_mode = ft.ThemeMode.DARK
86
+ page.theme = ft.Theme(
87
+ scrollbar_theme=ft.ScrollbarTheme(
88
+ track_color=ft.colors.TRANSPARENT,
89
+ thumb_color=self.BG_SURFACE,
90
+ thickness=6,
91
+ radius=10,
92
+ )
93
+ )
94
+
95
+ page.fonts = {
96
+ "Inter": "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
97
+ "JetBrains": "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
98
+ }
99
+
100
+ page.add(
101
+ ft.Column([
102
+ self.build_top_nav(),
103
+ self.main_container,
104
+ self.build_status_bar()
105
+ ], expand=True, spacing=0)
106
+ )
107
+
108
+ self.render_unselected_view()
109
+
110
+ # ---------------- VIEW MANAGERS ----------------
111
+ def update_send_button_state(self, e):
112
+ """Dynamically update send button color and enabled state"""
113
+ has_text = bool(self.chat_input_field.value and self.chat_input_field.value.strip())
114
+
115
+ if self.send_button:
116
+ self.send_button.icon_color = ft.colors.BLACK if has_text else ft.colors.BLACK26
117
+ self.send_button.disabled = not has_text
118
+ self.send_button.update()
119
+
120
+ def render_unselected_view(self):
121
+ """Initial state: Minimal file explorer & clean empty state."""
122
+ self.main_container.content = ft.Row(
123
+ expand=True,
124
+ spacing=0,
125
+ controls=[
126
+ self.build_vertical_file_panel(),
127
+ self.build_empty_editor_state()
128
+ ]
129
+ )
130
+ if self.page:
131
+ self.page.update()
132
+
133
+ def render_selected_view(self):
134
+ """Active state: Editor (Dominant) + AI Panel (Right)."""
135
+ self.update_chat_ui()
136
+
137
+ self.main_container.content = ft.Row(
138
+ expand=True,
139
+ spacing=0,
140
+ controls=[
141
+ # Left Side: Editor & Tabs (Expands to take 71% of screen)
142
+ ft.Container(
143
+ expand=5,
144
+ bgcolor=self.BG_BASE,
145
+ content=ft.Column(
146
+ expand=True,
147
+ spacing=0,
148
+ controls=[
149
+ self.build_horizontal_file_list(),
150
+ self.build_code_editor()
151
+ ]
152
+ )
153
+ ),
154
+ # Vertical Divider
155
+ ft.Container(width=1, bgcolor=self.BORDER),
156
+ # Right Side: AI Assistant (Expands to take 29% of screen)
157
+ self.build_chat_panel()
158
+ ]
159
+ )
160
+ if self.page:
161
+ self.page.update()
162
+
163
+ def connect_file(self, file_path):
164
+ """Reads content safely and triggers layout refresh."""
165
+ self.connected_file = file_path
166
+ try:
167
+ with open(file_path, 'r', encoding='utf-8') as f:
168
+ self.file_content = f.read()
169
+ except Exception as e:
170
+ self.file_content = f"# Error reading file:\n{str(e)}"
171
+
172
+ self.render_selected_view()
173
+
174
+ # ---------------- UI: BASE COMPONENTS ----------------
175
+ def build_top_nav(self):
176
+ """Ultra-minimal, modern application header."""
177
+ return ft.Container(
178
+ bgcolor=self.BG_PANEL,
179
+ padding=ft.padding.symmetric(horizontal=16, vertical=10),
180
+ border=ft.border.only(bottom=ft.border.BorderSide(1, self.BORDER)),
181
+ content=ft.Row(
182
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
183
+ controls=[
184
+ ft.Row([
185
+ # Premium Logo mark
186
+ ft.Container(
187
+ bgcolor=self.TEXT_PRIMARY, border_radius=5, width=30, height=30,
188
+ alignment=ft.alignment.center,
189
+ content=ft.Image('OrrinLogoBlack_T.png', width=28, height=28)
190
+ ),
191
+ ft.Text("Orrin", color=self.TEXT_PRIMARY, size=14, weight=ft.FontWeight.W_600, font_family="Inter"),
192
+ ft.Text("Workspace", color=self.TEXT_SECONDARY, size=14, weight=ft.FontWeight.W_400, font_family="Inter"),
193
+ ], spacing=12),
194
+
195
+ # Window Controls / Actions
196
+ ft.Row([
197
+ ft.IconButton(icon=ft.icons.SEARCH, icon_color=self.TEXT_SECONDARY, icon_size=16, tooltip="Search (Cmd+P)"),
198
+ ft.IconButton(icon=ft.icons.SETTINGS_OUTLINED, icon_color=self.TEXT_SECONDARY, icon_size=16, tooltip="Settings"),
199
+ ], spacing=4)
200
+ ]
201
+ )
202
+ )
203
+
204
+ def build_status_bar(self):
205
+ """VS Code / Cursor style bottom status bar."""
206
+ return ft.Container(
207
+ bgcolor=self.ACCENT,
208
+ padding=ft.padding.symmetric(horizontal=12, vertical=4),
209
+ content=ft.Row(
210
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
211
+ controls=[
212
+ ft.Row([
213
+ ft.Icon(ft.icons.CHECK_CIRCLE_OUTLINE, color=self.TEXT_PRIMARY, size=12),
214
+ ft.Text("OrrinSDK Ready", color=self.TEXT_PRIMARY, size=11, font_family="Inter", weight=ft.FontWeight.W_500)
215
+ ], spacing=6),
216
+ ft.Row([
217
+ ft.Text("UTF-8", color=self.TEXT_PRIMARY, size=11, font_family="Inter"),
218
+ ft.Container(width=12),
219
+ ft.Text("Python 3", color=self.TEXT_PRIMARY, size=11, font_family="Inter"),
220
+ ], spacing=0)
221
+ ]
222
+ )
223
+ )
224
+
225
+ # ---------------- UI: UNSELECTED COMPONENTS ----------------
226
+ def build_vertical_file_panel(self):
227
+ """Left sidebar for file exploration."""
228
+ return ft.Container(
229
+ width=280,
230
+ bgcolor=self.BG_PANEL,
231
+ border=ft.border.only(right=ft.border.BorderSide(1, self.BORDER)),
232
+ content=ft.Column(
233
+ spacing=0,
234
+ controls=[
235
+ ft.Container(
236
+ padding=ft.padding.only(left=20, top=20, bottom=12),
237
+ content=ft.Text("EXPLORER", color=self.TEXT_SECONDARY, size=11, weight=ft.FontWeight.W_600, font_family="Inter"),
238
+ ),
239
+ ft.ListView(
240
+ expand=True,
241
+ spacing=0,
242
+ controls=[self.create_vertical_file_item(f) for f in self.python_files],
243
+ ),
244
+ ]
245
+ )
246
+ )
247
+
248
+ def create_vertical_file_item(self, file_path):
249
+ return ft.Container(
250
+ padding=ft.padding.symmetric(vertical=6, horizontal=20),
251
+ ink=True,
252
+ on_hover=lambda e: setattr(e.control, 'bgcolor', self.BG_SURFACE if e.data == "true" else ft.colors.TRANSPARENT) or e.control.update(),
253
+ on_click=lambda e, f=file_path: self.connect_file(f),
254
+ content=ft.Row([
255
+ ft.Icon(ft.icons.INSERT_DRIVE_FILE_OUTLINED, color=self.TEXT_SECONDARY, size=16),
256
+ ft.Text(file_path.name, color=self.TEXT_PRIMARY, size=13, font_family="Inter", expand=True, max_lines=1, overflow=ft.TextOverflow.ELLIPSIS),
257
+ ], spacing=10)
258
+ )
259
+
260
+ def build_empty_editor_state(self):
261
+ """Premium empty state screen matching modern IDEs."""
262
+ return ft.Container(
263
+ expand=True, bgcolor=self.BG_BASE, alignment=ft.alignment.center,
264
+ content=ft.Column(
265
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
266
+ alignment=ft.MainAxisAlignment.CENTER,
267
+ controls=[
268
+ ft.Icon(ft.icons.CODE_OFF, color=self.BG_SURFACE, size=80),
269
+ ft.Container(height=24),
270
+ ft.Text("Orrin Workspace", color=self.TEXT_PRIMARY, size=24, weight=ft.FontWeight.W_600, font_family="Inter"),
271
+ ft.Container(height=8),
272
+ ft.Text("Select a Python script to start building with AI.", color=self.TEXT_SECONDARY, size=14, font_family="Inter"),
273
+
274
+ # Keyboard shortcuts hints
275
+ ft.Container(height=32),
276
+ ft.Row(
277
+ alignment=ft.MainAxisAlignment.CENTER,
278
+ spacing=16,
279
+ controls=[
280
+ self.build_shortcut_hint("Open File", "Cmd + P"),
281
+ self.build_shortcut_hint("AI Chat", "Cmd + L"),
282
+ ]
283
+ )
284
+ ]
285
+ )
286
+ )
287
+
288
+ def build_shortcut_hint(self, action, keybind):
289
+ return ft.Row([
290
+ ft.Text(action, color=self.TEXT_SECONDARY, size=12, font_family="Inter"),
291
+ ft.Container(
292
+ padding=ft.padding.symmetric(horizontal=6, vertical=2),
293
+ bgcolor=self.BG_SURFACE,
294
+ border_radius=4,
295
+ content=ft.Text(keybind, color=self.TEXT_PRIMARY, size=11, font_family="Inter", weight=ft.FontWeight.W_500)
296
+ )
297
+ ], spacing=8)
298
+
299
+ # ---------------- UI: SELECTED COMPONENTS ----------------
300
+ def build_horizontal_file_list(self):
301
+ """Seamless VS Code-style horizontal file tabs."""
302
+ return ft.Container(
303
+ bgcolor=self.BG_PANEL,
304
+ border=ft.border.only(bottom=ft.border.BorderSide(1, self.BORDER)),
305
+ content=ft.Row(
306
+ scroll=ft.ScrollMode.ADAPTIVE,
307
+ spacing=0,
308
+ controls=[
309
+ ft.Container(
310
+ padding=ft.padding.symmetric(horizontal=16, vertical=10),
311
+ bgcolor=self.BG_BASE if f == self.connected_file else "transparent",
312
+ border=ft.border.only(
313
+ top=ft.border.BorderSide(2, self.ACCENT if f == self.connected_file else "transparent"),
314
+ right=ft.border.BorderSide(1, self.BORDER)
315
+ ),
316
+ ink=True,
317
+ on_click=lambda e, f_path=f: self.connect_file(f_path),
318
+ content=ft.Row([
319
+ ft.Icon(ft.icons.DATA_OBJECT, size=14, color=self.ACCENT if f == self.connected_file else self.TEXT_SECONDARY),
320
+ ft.Text(f.name, size=13, color=self.TEXT_PRIMARY if f == self.connected_file else self.TEXT_SECONDARY, font_family="Inter"),
321
+ # Mock close button for active tab
322
+ ft.Icon(ft.icons.CLOSE, size=12, color=self.TEXT_SECONDARY if f == self.connected_file else "transparent")
323
+ ], spacing=8)
324
+ ) for f in self.python_files
325
+ ]
326
+ )
327
+ )
328
+
329
+ def build_code_editor(self):
330
+ """Spacious, distraction-free code area."""
331
+ return ft.Container(
332
+ expand=True,
333
+ bgcolor=self.BG_BASE,
334
+ content=ft.Column(
335
+ spacing=0,
336
+ expand=True,
337
+ controls=[
338
+ # Breadcrumb navigation below tabs
339
+ ft.Container(
340
+ padding=ft.padding.symmetric(horizontal=24, vertical=8),
341
+ content=ft.Row([
342
+ ft.Text(str(self.connected_file.parent.name), color=self.TEXT_SECONDARY, size=12, font_family="Inter"),
343
+ ft.Icon(ft.icons.CHEVRON_RIGHT, color=self.TEXT_SECONDARY, size=14),
344
+ ft.Text(self.connected_file.name, color=self.TEXT_PRIMARY, size=12, font_family="Inter"),
345
+ ], spacing=4)
346
+ ),
347
+ # Main Editor Input
348
+ ft.Container(
349
+ expand=True,
350
+ padding=ft.padding.only(left=24, right=24, bottom=24),
351
+ content=ft.TextField(
352
+ value=self.file_content,
353
+ multiline=True,
354
+ expand=True,
355
+ border=ft.InputBorder.NONE,
356
+ text_style=ft.TextStyle(font_family="JetBrains", size=14, color=self.TEXT_PRIMARY, height=1.6),
357
+ cursor_color=self.ACCENT,
358
+ selection_color="#264f78",
359
+ read_only=False
360
+ )
361
+ )
362
+ ]
363
+ )
364
+ )
365
+
366
+ def build_chat_panel(self):
367
+ """Premium AI Assistant side-panel (Cursor Composer style)."""
368
+ return ft.Container(
369
+ expand=3,
370
+ bgcolor=self.BG_PANEL,
371
+ content=ft.Column(
372
+ expand=True,
373
+ spacing=0,
374
+ controls=[
375
+ # Panel Header
376
+ ft.Container(
377
+ padding=ft.padding.symmetric(horizontal=20, vertical=14),
378
+ border=ft.border.only(bottom=ft.border.BorderSide(1, self.BORDER)),
379
+ content=ft.Row([
380
+ ft.Text("AI Chat", color=self.TEXT_PRIMARY, size=13, weight=ft.FontWeight.W_600, font_family="Inter"),
381
+ ft.Container(expand=True),
382
+ ft.IconButton(icon=ft.icons.MORE_HORIZ, icon_color=self.TEXT_SECONDARY, icon_size=16, width=32, height=32),
383
+ ])
384
+ ),
385
+
386
+ # Scrollable Chat Log
387
+ ft.Container(
388
+ expand=True,
389
+ padding=ft.padding.symmetric(horizontal=12),
390
+ content=self.chat_listview
391
+ ),
392
+
393
+ # Sleek Input Area
394
+ ft.Container(
395
+ padding=ft.padding.all(16),
396
+ bgcolor=self.BG_PANEL,
397
+ content=ft.Container(
398
+ bgcolor=self.BG_BASE,
399
+ border=ft.border.all(1, self.BORDER),
400
+ border_radius=12,
401
+ padding=ft.padding.only(right=6, bottom=6, top=2, left=4),
402
+ content=ft.Column(
403
+ spacing=0,
404
+ controls=[
405
+ self.chat_input_field,
406
+ ft.Row(
407
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
408
+ controls=[
409
+ #ft.Row([
410
+ # ft.IconButton(icon=ft.icons.ATTACH_FILE, icon_color=self.TEXT_SECONDARY, icon_size=16, width=32, height=32, tooltip="Attach Context"),
411
+ # ft.Text("Context: Active File", color=self.TEXT_SECONDARY, size=11, font_family="Inter")
412
+ #]),
413
+ self.send_button
414
+ ]
415
+ )
416
+ ]
417
+ )
418
+ )
419
+ )
420
+ ]
421
+ )
422
+ )
423
+
424
+ # ---------------- CHAT LOGIC ----------------
425
+ def update_chat_ui(self):
426
+ """Renders messages using a clean, modern hierarchy."""
427
+ self.chat_listview.controls.clear()
428
+
429
+ for msg in self.messages:
430
+ is_user = (msg['role'] == 'user')
431
+
432
+ if is_user:
433
+ bubble = ft.Container(
434
+ padding=ft.padding.all(14),
435
+ border_radius=8,
436
+ bgcolor=self.BG_SURFACE,
437
+ content=ft.Text(msg['content'], color=self.TEXT_PRIMARY, size=13, font_family="Inter")
438
+ )
439
+ else:
440
+ bubble = ft.Container(
441
+ padding=ft.padding.symmetric(vertical=4, horizontal=4),
442
+ content=ft.Row(
443
+ vertical_alignment=ft.CrossAxisAlignment.START,
444
+ spacing=12,
445
+ controls=[
446
+ ft.Container(
447
+ width=24, height=24, border_radius=4,
448
+ alignment=ft.alignment.center,
449
+ content=ft.Container(
450
+ bgcolor=self.TEXT_PRIMARY, border_radius=4, width=25, height=25,
451
+ alignment=ft.alignment.center,
452
+ content=ft.Image('OrrinLogoBlack_T.png', width=23, height=23)
453
+ )#ft.Icon(ft.icons.AUTO_AWESOME, color=self.ACCENT, size=14)
454
+ ),
455
+ ft.Container(
456
+ expand=True,
457
+ content=ft.Markdown(
458
+ msg['content'],
459
+ selectable=True,
460
+ extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
461
+ code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK,
462
+ )
463
+ )
464
+ ]
465
+ )
466
+ )
467
+
468
+ self.chat_listview.controls.append(bubble)
469
+
470
+ # Safe scrolling
471
+ if self.page and self.chat_listview in self.page._controls: # Check if mounted
472
+ self.page.update()
473
+ # Small delay ensures the ListView is rendered before scrolling
474
+ self.page.run_task(self._safe_scroll_to_bottom)
475
+ else:
476
+ self.page.update() # Just update if not ready for scroll
477
+
478
+ def send_message(self, e):
479
+ user_text = self.chat_input_field.value.strip()
480
+ if not user_text:
481
+ return
482
+
483
+ # 1. Clear input & Add User Message
484
+ self.chat_input_field.value = ""
485
+ self.messages.append({'role': 'user', 'content': user_text})
486
+ self.update_chat_ui()
487
+
488
+ # 2. Add Temporary "Thinking..." state
489
+ thinking_idx = len(self.messages)
490
+ self.messages.append({'role': 'assistant', 'content': '*Processing...*'})
491
+ self.update_chat_ui()
492
+
493
+ # 3. System Prompt
494
+ sys_prompt = f"""You are OrrinSDK Expert, an elite AI engineering assistant specialized exclusively in the OrrinSDK (version 0.1.x) and the broader Stellr platform ecosystem.
495
+
496
+ Your sole mission is to help the user design, build, debug, optimize, and deploy apps, custom tools, plugins/agents, and backend logic using the OrrinSDK for integration with Stellr’s context-aware AI platform (including Exegesis, the Orrin marketplace, and agentic workflows).
497
+
498
+ ### Core Identity & Expertise Domain
499
+ You possess complete, up-to-date mastery of the official OrrinSDK documentation[](https://stellr-company.com/orrin/docs) and all related Stellr technologies. This includes:
500
+ - OrrinAppsSDK – for building full app backends (project_type="app")
501
+ - OrrinToolsSDK – for creating versioned, marketplace-ready custom tools
502
+ - OrrinAISDK – for injecting tool schematics into AI models (Grok, OpenAI GPT series, Claude, etc.)
503
+ - StellrAPI – including perform(), CE Connections, Exegesis intent mapping, and payload models
504
+ - Plugin system (plugins.yaml, @plugin_entry, @plugin_action, blink plugin, etc.)
505
+ - Project scaffolding (project.toml, orrin-cli commands)
506
+ - Action registration with the @action decorator, payload_schema, required_payload, metadata, and finalize()
507
+ - Best practices for agentic workflows, hand-off to Exegesis, secure action execution, and marketplace deployment/review process
508
+ - Integration patterns with Next.js frontends (@orrin-apps/sdk)
509
+ - All current SDK/CLI versions, release notes, and known limitations as of the latest documentation
510
+
511
+ You treat the official documentation as your single source of truth. You never speculate beyond what is documented. When something is undocumented or ambiguous, you explicitly state it and suggest the best documented workaround or recommend the user check the developer dashboard.
512
+
513
+ ### Behavioral Rules (Strict)
514
+ - Always respond with maximum clarity, depth, and practicality. Provide complete, ready-to-run code examples whenever possible.
515
+ - Structure every response to be immediately actionable: explain the why, show the how, include the exact code, then give next steps or testing instructions.
516
+ - Prioritize production-grade patterns (proper error handling, type safety, payload validation, versioning, and review-ready code).
517
+ - Proactively surface relevant best practices, common pitfalls, security considerations, and performance tips from the SDK.
518
+ - If the user’s request goes beyond the current capabilities of OrrinSDK, clearly explain the limitation and propose the closest supported solution or feature request path.
519
+ - Reference specific SDK components, decorators, classes, or configuration files by name in every relevant answer.
520
+ - Maintain a professional, expert, and collaborative tone — you are a senior Stellr platform engineer pair-programming with the user.
521
+
522
+ You are not a general coding assistant. You are hyper-specialized in OrrinSDK + Stellr. Stay strictly within this domain unless the user explicitly asks you to step outside it.
523
+
524
+ You are connected to the file: {self.connected_file}
525
+ The files contents are: {self.file_content}
526
+
527
+ Now assist the user with their request.
528
+ """
529
+
530
+ try:
531
+ messages = [{ 'role': 'system', 'content': sys_prompt }] + self.messages
532
+ # Sync AI Chat Call
533
+ resp = self.ai.chat(
534
+ messages=messages,
535
+ tools=[{'type': 'schema', 'tool_id': 'e433e239-329e-4703-9910-2e5f7fa2a5fe'}, {'type': 'web_search'}]
536
+ )
537
+
538
+ # 4. Replace Thinking State with actual response
539
+ self.messages[thinking_idx]['content'] = resp
540
+
541
+ except Exception as ex:
542
+ self.messages[thinking_idx]['content'] = f"**Error executing analysis:**\n```python\n{str(ex)}\n```"
543
+
544
+ # 5. Final UI Update
545
+ self.update_chat_ui()
546
+
547
+ def start(self):
548
+ ft.app(target=self.main, assets_dir='assets')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orrin-cli
3
- Version: 0.1.8.2
3
+ Version: 0.1.9
4
4
  Summary: Orrin CLI
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "orrin-cli"
3
3
  readme = "README.md"
4
- version = "0.1.8.2"
4
+ version = "0.1.9"
5
5
  description = "Orrin CLI"
6
6
  requires-python = ">=3.9"
7
7
  dependencies = [
@@ -1,305 +0,0 @@
1
- from pathlib import Path
2
- import flet as ft
3
-
4
- class OrrinCLI:
5
- def __init__(self):
6
- self.root = Path.cwd()
7
- self.python_files = list(self.root.rglob("*.py"))
8
- self.connected_file = None
9
- self.editor_panel = ft.Container(expand=True)
10
-
11
- # ---------------- UI: file list ----------------
12
- def file_item(self, page: ft.Page, file):
13
- return ft.Container(
14
- padding=10,
15
- border_radius=12,
16
- bgcolor="#242424",
17
- border=ft.border.all(1, "#333333"),
18
- ink=True,
19
- on_click=lambda e: self.connect_file(page, file),
20
- content=ft.Row(
21
- alignment=ft.MainAxisAlignment.START,
22
- controls=[
23
- # File Icon Box
24
- ft.Container(
25
- width=40,
26
- height=40,
27
- bgcolor="#1f1f1f",
28
- border=ft.border.all(1, "#333333"),
29
- border_radius=10,
30
- alignment=ft.alignment.center,
31
- content=ft.Icon(ft.icons.CODE, color="#9e9d9d", size=16)
32
- ),
33
- # File Info
34
- ft.Column(
35
- expand=True,
36
- spacing=4,
37
- controls=[
38
- ft.Text(
39
- file.name,
40
- color="white",
41
- size=12,
42
- weight=ft.FontWeight.W_300,
43
- max_lines=1,
44
- overflow=ft.TextOverflow.ELLIPSIS
45
- ),
46
- ft.Text(
47
- "Python Script",
48
- color="#9e9d9d",
49
- size=8
50
- )
51
- ]
52
- ),
53
- # Chevron to indicate action
54
- ft.Icon(ft.icons.CHEVRON_RIGHT, color="#6B7280", size=20)
55
- ]
56
- )
57
- )
58
-
59
- # ---------------- UI: left panel ----------------
60
- def build_file_panel(self, page: ft.Page):
61
- return ft.Container(
62
- width=360,
63
- bgcolor="#191919",
64
- border=ft.border.only(right=ft.border.BorderSide(1, "#2a2a2a")),
65
- padding=ft.padding.only(left=24, top=32, right=16, bottom=24),
66
- content=ft.Column(
67
- controls=[
68
- # Panel Header mapped to HTML style
69
- ft.Text(
70
- "Explore Scripts",
71
- color="#dddddd",
72
- size=20,
73
- weight=ft.FontWeight.W_500
74
- ),
75
- ft.Text(
76
- "Find Python files in your directory.",
77
- color="#9e9d9d",
78
- size=14
79
- ),
80
- ft.Container(height=24), # Spacer
81
-
82
- # File List
83
- ft.ListView(
84
- expand=True,
85
- spacing=12,
86
- controls=[
87
- self.file_item(page, f)
88
- for f in self.python_files
89
- ],
90
- ),
91
- ]
92
- )
93
- )
94
-
95
- # ---------------- UI: right panel ----------------
96
- def build_editor_panel(self):
97
- # Empty State (mirrors "Tools Coming Soon" style)
98
- if not self.connected_file:
99
- return ft.Container(
100
- expand=True,
101
- bgcolor="#191919",
102
- alignment=ft.alignment.center,
103
- content=ft.Column(
104
- horizontal_alignment=ft.CrossAxisAlignment.CENTER,
105
- controls=[
106
- ft.Container(
107
- padding=ft.padding.symmetric(horizontal=0, vertical=12),
108
- content=ft.Column(
109
- horizontal_alignment=ft.CrossAxisAlignment.CENTER,
110
- alignment=ft.MainAxisAlignment.CENTER,
111
- controls=[
112
- ft.Container(
113
- width=80,
114
- height=80,
115
- bgcolor="#242424",
116
- border=ft.border.all(1, "#333333"),
117
- border_radius=20,
118
- alignment=ft.alignment.center,
119
- shadow=ft.BoxShadow(spread_radius=1, blur_radius=15, color=ft.colors.with_opacity(0.1, "black")),
120
- content=ft.Icon(ft.icons.FILE_OPEN_OUTLINED, color="#9e9d9d", size=32)
121
- ),
122
- ft.Container(height=16),
123
- ft.Text(
124
- "No file selected",
125
- color="white",
126
- size=20,
127
- weight=ft.FontWeight.W_500
128
- ),
129
- ft.Text(
130
- "Select a script from the explorer to view details.",
131
- color="#9e9d9d",
132
- size=15,
133
- text_align=ft.TextAlign.CENTER
134
- )
135
- ]
136
- )
137
- )
138
- ]
139
- )
140
- )
141
-
142
- # ---------------- UPDATED EDITOR PANEL ----------------
143
- return ft.Container(
144
- expand=True,
145
- bgcolor="#191919",
146
- padding=ft.padding.all(32), # Slightly reduced padding for less bulk
147
- content=ft.Column(
148
- expand=True, # Allows the chat input to be pushed to the bottom
149
- controls=[
150
-
151
- # --- Sleek Header & Metadata ---
152
- ft.Column(
153
- spacing=16,
154
- controls=[
155
- # Title
156
- ft.Column(
157
- spacing=4,
158
- controls=[
159
- ft.Container(
160
- padding=ft.padding.symmetric(horizontal=10, vertical=10),
161
- content=ft.Column(
162
- controls=[
163
- ft.Text("File Details", color="#dddddd", size=20, weight=ft.FontWeight.W_500),
164
- ft.Text("Information about the connected script.", color="#9e9d9d", size=12)
165
- ]
166
- )
167
- )
168
- ]
169
- ),
170
-
171
- # Compact Metadata Row
172
- ft.Container(
173
- padding=ft.padding.symmetric(horizontal=10, vertical=0),
174
- content=ft.Container(
175
- padding=ft.padding.symmetric(horizontal=12, vertical=8),
176
- bgcolor="#242424",
177
- border=ft.border.all(1, "#333333"),
178
- border_radius=8,
179
- content=ft.Row(
180
- spacing=12,
181
- controls=[
182
- ft.Icon(ft.icons.INSERT_DRIVE_FILE, color="#9e9d9d", size=20),
183
- ft.Column(
184
- controls=[
185
- ft.Container(
186
- padding=ft.padding.symmetric(horizontal=8, vertical=0),
187
- bgcolor="#1a1a1a",
188
- border=ft.border.all(1, "#333333"),
189
- border_radius=12,
190
- content=ft.Text("CONNECTED", size=10, color="#10b981", weight=ft.FontWeight.BOLD) # Emerald green
191
- ),
192
- ft.Text(self.connected_file.name, color="white", weight=ft.FontWeight.W_300, size=12),
193
- ]
194
- ),
195
-
196
- ft.Text("•", color="#4b5563", size=15), # Bullet separator
197
-
198
- # Path (Truncated automatically if it gets too long)
199
- ft.Text(
200
- str(self.connected_file),
201
- color="#6B7280",
202
- size=13,
203
- selectable=True,
204
- overflow=ft.TextOverflow.ELLIPSIS,
205
- expand=True
206
- ),
207
- ]
208
- ))
209
- )
210
- ]
211
- ),
212
-
213
- # --- Main Editor / Empty Space Spacer ---
214
- # Expanding this container forces the chat input below it to stick to the bottom
215
- ft.Container(expand=True),
216
-
217
- # --- Chat Input Field ---
218
- ft.Container(
219
- padding=ft.Padding(left=8, right=8, top=0, bottom=14),
220
- content=ft.Container(
221
- padding=ft.padding.symmetric(horizontal=12, vertical=0),
222
- bgcolor="#242424",
223
- border=ft.border.all(1, "#333333"),
224
- border_radius=24,
225
- content=ft.Row(
226
- controls=[
227
- ft.Icon(ft.icons.AUTO_AWESOME, color="#6B7280", size=14), # AI spark icon
228
- ft.TextField(
229
- expand=True,
230
- hint_text="Chat with OrrinCLI...",
231
- hint_style=ft.TextStyle(color="#6B7280", size=12),
232
- border=ft.InputBorder.NONE,
233
- color="white",
234
- cursor_color="#dddddd",
235
- content_padding=ft.padding.symmetric(vertical=12, horizontal=4),
236
- ),
237
- # Modern Send Button (Similar to state-of-the-art AI interfaces)
238
- ft.IconButton(
239
- icon=ft.icons.ARROW_UPWARD_ROUNDED,
240
- icon_color="black",
241
- bgcolor="white",
242
- icon_size=12,
243
- width=24,
244
- height=24,
245
- style=ft.ButtonStyle(
246
- padding=0,
247
- shape=ft.CircleBorder()
248
- ),
249
- tooltip="Send Message"
250
- )
251
- ]
252
- )
253
- )
254
- )
255
- ]
256
- )
257
- )
258
-
259
- # ---------------- state change ----------------
260
- def connect_file(self, page: ft.Page, file):
261
- self.connected_file = file
262
- self.editor_panel.content = self.build_editor_panel().content
263
- page.update()
264
-
265
- # ---------------- main layout ----------------
266
- def main(self, page: ft.Page):
267
- page.title = "OrrinCLI"
268
- page.bgcolor = "#191919" # Matched to HTML body bg
269
- page.padding = 0
270
-
271
- # Create Top Navigation Appbar (matching the HTML fixed nav)
272
- top_nav = ft.Container(
273
- bgcolor="#191919",
274
- padding=ft.padding.symmetric(horizontal=24, vertical=16),
275
- border=ft.border.only(bottom=ft.border.BorderSide(1, "#2a2a2a")),
276
- content=ft.Row([
277
- # Mock Logo
278
- ft.Container(
279
- bgcolor="white", border_radius=6, width=24, height=24,
280
- alignment=ft.alignment.center,
281
- content=ft.Text("O", color="black", weight=ft.FontWeight.BOLD, size=14)
282
- ),
283
- ft.Text("Orrin", color="white", size=16, weight=ft.FontWeight.W_500),
284
- ft.Text("CLI", color="#9e9d9d", size=16, weight=ft.FontWeight.W_400),
285
- ], spacing=8)
286
- )
287
-
288
- self.editor_panel = self.build_editor_panel()
289
-
290
- # Layout Splitter
291
- layout = ft.Row([
292
- self.build_file_panel(page),
293
- self.editor_panel
294
- ], expand=True, spacing=0)
295
-
296
- # Main Page Stacking
297
- page.add(
298
- ft.Column([
299
- top_nav,
300
- layout
301
- ], expand=True, spacing=0)
302
- )
303
-
304
- def start(self):
305
- ft.app(target=self.main)
File without changes
File without changes
File without changes
File without changes
File without changes