@benzsiangco/jarvis 1.0.2 → 1.1.0

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.
Files changed (53) hide show
  1. package/dist/cli.js +478 -347
  2. package/dist/electron/main.js +160 -0
  3. package/dist/electron/preload.js +19 -0
  4. package/package.json +19 -6
  5. package/skills.md +147 -0
  6. package/src/agents/index.ts +248 -0
  7. package/src/brain/loader.ts +136 -0
  8. package/src/cli.ts +411 -0
  9. package/src/config/index.ts +363 -0
  10. package/src/core/executor.ts +222 -0
  11. package/src/core/plugins.ts +148 -0
  12. package/src/core/types.ts +217 -0
  13. package/src/electron/main.ts +192 -0
  14. package/src/electron/preload.ts +25 -0
  15. package/src/electron/types.d.ts +20 -0
  16. package/src/index.ts +12 -0
  17. package/src/providers/antigravity-loader.ts +233 -0
  18. package/src/providers/antigravity.ts +585 -0
  19. package/src/providers/index.ts +523 -0
  20. package/src/sessions/index.ts +194 -0
  21. package/src/tools/index.ts +436 -0
  22. package/src/tui/index.tsx +784 -0
  23. package/src/utils/auth-prompt.ts +394 -0
  24. package/src/utils/index.ts +180 -0
  25. package/src/utils/native-picker.ts +71 -0
  26. package/src/utils/skills.ts +99 -0
  27. package/src/utils/table-integration-examples.ts +617 -0
  28. package/src/utils/table-utils.ts +401 -0
  29. package/src/web/build-ui.ts +27 -0
  30. package/src/web/server.ts +674 -0
  31. package/src/web/ui/dist/.gitkeep +0 -0
  32. package/src/web/ui/dist/main.css +1 -0
  33. package/src/web/ui/dist/main.js +320 -0
  34. package/src/web/ui/dist/main.js.map +20 -0
  35. package/src/web/ui/index.html +46 -0
  36. package/src/web/ui/src/App.tsx +143 -0
  37. package/src/web/ui/src/Modules/Safety/GuardianModal.tsx +83 -0
  38. package/src/web/ui/src/components/Layout/ContextPanel.tsx +243 -0
  39. package/src/web/ui/src/components/Layout/Header.tsx +91 -0
  40. package/src/web/ui/src/components/Layout/ModelSelector.tsx +235 -0
  41. package/src/web/ui/src/components/Layout/SessionStats.tsx +369 -0
  42. package/src/web/ui/src/components/Layout/Sidebar.tsx +895 -0
  43. package/src/web/ui/src/components/Modules/Chat/ChatStage.tsx +620 -0
  44. package/src/web/ui/src/components/Modules/Chat/MessageItem.tsx +446 -0
  45. package/src/web/ui/src/components/Modules/Editor/CommandInspector.tsx +71 -0
  46. package/src/web/ui/src/components/Modules/Editor/DiffViewer.tsx +83 -0
  47. package/src/web/ui/src/components/Modules/Terminal/TabbedTerminal.tsx +202 -0
  48. package/src/web/ui/src/components/Settings/SettingsModal.tsx +935 -0
  49. package/src/web/ui/src/config/models.ts +70 -0
  50. package/src/web/ui/src/main.tsx +13 -0
  51. package/src/web/ui/src/store/agentStore.ts +41 -0
  52. package/src/web/ui/src/store/uiStore.ts +64 -0
  53. package/src/web/ui/src/types/index.ts +54 -0
@@ -0,0 +1,148 @@
1
+ // Plugin system for Jarvis
2
+ // Compatible with opencode plugins like opencode-antigravity-auth
3
+ import { join } from 'path';
4
+ import { existsSync, readFileSync } from 'fs';
5
+ import { getConfigDir } from '../config';
6
+ import type { Plugin, JarvisInstance, ProviderConfig, Tool, AgentConfig } from '../core/types';
7
+
8
+ // Plugin registry
9
+ const plugins = new Map<string, Plugin>();
10
+
11
+ export interface PluginLoader {
12
+ name: string;
13
+ load: () => Promise<Plugin>;
14
+ }
15
+
16
+ // Load plugins from config
17
+ export async function loadPlugins(pluginNames: string[]): Promise<Plugin[]> {
18
+ const loadedPlugins: Plugin[] = [];
19
+
20
+ for (const name of pluginNames) {
21
+ try {
22
+ const plugin = await loadPlugin(name);
23
+ if (plugin) {
24
+ plugins.set(name, plugin);
25
+ loadedPlugins.push(plugin);
26
+ }
27
+ } catch (error) {
28
+ console.warn(`Failed to load plugin ${name}:`, error);
29
+ }
30
+ }
31
+
32
+ return loadedPlugins;
33
+ }
34
+
35
+ // Load a single plugin
36
+ async function loadPlugin(name: string): Promise<Plugin | undefined> {
37
+ // Parse plugin name (supports @scope/name@version format)
38
+ const { packageName, version } = parsePluginName(name);
39
+
40
+ // Check if it's an opencode plugin that needs compatibility
41
+ if (packageName.includes('opencode-antigravity-auth')) {
42
+ return createAntigravityCompatPlugin(packageName);
43
+ }
44
+
45
+ // Try to dynamically import the plugin
46
+ try {
47
+ const module = await import(packageName);
48
+ if (module.default && typeof module.default === 'object') {
49
+ return module.default as Plugin;
50
+ }
51
+ return module as Plugin;
52
+ } catch (error) {
53
+ console.warn(`Could not load plugin ${packageName}:`, error);
54
+ return undefined;
55
+ }
56
+ }
57
+
58
+ // Parse plugin name to extract package and version
59
+ function parsePluginName(name: string): { packageName: string; version?: string } {
60
+ // Handle @scope/package@version format
61
+ const atIndex = name.lastIndexOf('@');
62
+ if (atIndex > 0) {
63
+ return {
64
+ packageName: name.substring(0, atIndex),
65
+ version: name.substring(atIndex + 1),
66
+ };
67
+ }
68
+ return { packageName: name };
69
+ }
70
+
71
+ // Create a compatibility layer for opencode-antigravity-auth
72
+ function createAntigravityCompatPlugin(packageName: string): Plugin {
73
+ return {
74
+ name: 'antigravity-auth-compat',
75
+ version: '1.0.0',
76
+
77
+ async init(jarvis: JarvisInstance) {
78
+ console.log('Antigravity Auth compatibility plugin initialized');
79
+
80
+ // Check for existing accounts file
81
+ const accountsPath = join(getConfigDir(), 'antigravity-accounts.json');
82
+ if (existsSync(accountsPath)) {
83
+ try {
84
+ const accounts = JSON.parse(readFileSync(accountsPath, 'utf-8'));
85
+ console.log(`Found ${accounts.accounts?.length || 0} Antigravity account(s)`);
86
+ } catch {
87
+ // Ignore parse errors
88
+ }
89
+ } else {
90
+ console.log('No Antigravity accounts found. Run "jarvis auth login" to add an account.');
91
+ }
92
+ },
93
+
94
+ // Antigravity models are already configured in default config
95
+ providers: [],
96
+ tools: [],
97
+ agents: [],
98
+ };
99
+ }
100
+
101
+ // Initialize all loaded plugins
102
+ export async function initializePlugins(jarvis: JarvisInstance): Promise<void> {
103
+ for (const [name, plugin] of plugins) {
104
+ try {
105
+ if (plugin.init) {
106
+ await plugin.init(jarvis);
107
+ }
108
+
109
+ // Register plugin providers
110
+ if (plugin.providers) {
111
+ for (const provider of plugin.providers) {
112
+ jarvis.providers.set(provider.id, provider);
113
+ }
114
+ }
115
+
116
+ // Register plugin tools
117
+ if (plugin.tools) {
118
+ for (const tool of plugin.tools) {
119
+ jarvis.tools.set(tool.name, tool);
120
+ }
121
+ }
122
+
123
+ // Register plugin agents
124
+ if (plugin.agents) {
125
+ for (const agent of plugin.agents) {
126
+ jarvis.agents.set(agent.id, agent);
127
+ }
128
+ }
129
+ } catch (error) {
130
+ console.error(`Error initializing plugin ${name}:`, error);
131
+ }
132
+ }
133
+ }
134
+
135
+ // Get a loaded plugin
136
+ export function getPlugin(name: string): Plugin | undefined {
137
+ return plugins.get(name);
138
+ }
139
+
140
+ // List all loaded plugins
141
+ export function listPlugins(): Plugin[] {
142
+ return Array.from(plugins.values());
143
+ }
144
+
145
+ // Unload a plugin
146
+ export function unloadPlugin(name: string): boolean {
147
+ return plugins.delete(name);
148
+ }
@@ -0,0 +1,217 @@
1
+ // Core types for Jarvis AI Agent
2
+ import type { z } from 'zod';
3
+
4
+ export type AgentMode = 'primary' | 'subagent' | 'all';
5
+
6
+ export type PermissionLevel = 'ask' | 'allow' | 'deny';
7
+
8
+ export interface AgentConfig {
9
+ id: string;
10
+ name: string;
11
+ description: string;
12
+ mode: AgentMode;
13
+ model?: string;
14
+ prompt?: string;
15
+ temperature?: number;
16
+ top_p?: number;
17
+ maxSteps?: number;
18
+ steps?: number;
19
+ tools?: Record<string, boolean>;
20
+ permission?: PermissionConfig;
21
+ hidden?: boolean;
22
+ disabled?: boolean;
23
+ color?: string;
24
+ options?: Record<string, unknown>;
25
+ }
26
+
27
+ export type PermissionRuleConfig = PermissionLevel | Record<string, PermissionLevel>;
28
+
29
+ export interface PermissionConfig {
30
+ read?: PermissionRuleConfig;
31
+ edit?: PermissionRuleConfig;
32
+ glob?: PermissionRuleConfig;
33
+ grep?: PermissionRuleConfig;
34
+ list?: PermissionRuleConfig;
35
+ bash?: PermissionRuleConfig;
36
+ task?: PermissionRuleConfig;
37
+ external_directory?: PermissionRuleConfig;
38
+ todowrite?: PermissionLevel;
39
+ todoread?: PermissionLevel;
40
+ question?: PermissionLevel;
41
+ webfetch?: PermissionLevel;
42
+ websearch?: PermissionLevel;
43
+ codesearch?: PermissionLevel;
44
+ lsp?: PermissionRuleConfig;
45
+ doom_loop?: PermissionLevel;
46
+ }
47
+
48
+ export interface ProviderConfig {
49
+ id: string;
50
+ name: string;
51
+ apiKey?: string;
52
+ baseUrl?: string;
53
+ models: Record<string, ModelConfig>;
54
+ }
55
+
56
+ export interface ModelConfig {
57
+ name: string;
58
+ limit: {
59
+ context: number;
60
+ output: number;
61
+ };
62
+ modalities?: {
63
+ input: string[];
64
+ output: string[];
65
+ };
66
+ variants?: Record<string, Record<string, unknown>>;
67
+ }
68
+
69
+ export interface JarvisConfig {
70
+ $schema?: string;
71
+ model?: string;
72
+ small_model?: string;
73
+ default_agent?: string;
74
+ theme?: string;
75
+ username?: string;
76
+ snapshot?: boolean;
77
+ share?: 'manual' | 'auto' | 'disabled';
78
+ autoupdate?: boolean | 'notify';
79
+ logLevel?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
80
+ provider?: Record<string, Partial<ProviderConfig>>;
81
+ agent?: Record<string, Partial<AgentConfig>>;
82
+ plugin?: string[];
83
+ tools?: Record<string, boolean>;
84
+ permission?: PermissionConfig;
85
+ keybinds?: Record<string, string>;
86
+ command?: Record<string, CommandConfig>;
87
+ watcher?: {
88
+ ignore?: string[];
89
+ };
90
+ tui?: {
91
+ scroll_speed?: number;
92
+ scroll_acceleration?: { enabled: boolean };
93
+ diff_style?: 'auto' | 'stacked';
94
+ };
95
+ server?: {
96
+ port?: number;
97
+ hostname?: string;
98
+ mdns?: boolean;
99
+ cors?: string[];
100
+ };
101
+ oauth?: {
102
+ callbackUrl?: string; // Custom OAuth callback URL for container deployments
103
+ callbackHost?: string; // Bind host for callback server (default: '0.0.0.0')
104
+ callbackPort?: number; // Port for callback server (default: 51121)
105
+ };
106
+ disabled_providers?: string[];
107
+ enabled_providers?: string[];
108
+ workspaces?: Array<{
109
+ id: string;
110
+ name: string;
111
+ path: string;
112
+ icon: string;
113
+ }>;
114
+ persona?: string;
115
+ instructions?: string;
116
+ restrictedPaths?: string[];
117
+ }
118
+
119
+ export interface CommandConfig {
120
+ template: string;
121
+ description?: string;
122
+ agent?: string;
123
+ model?: string;
124
+ subtask?: boolean;
125
+ }
126
+
127
+ export interface Message {
128
+ id: string;
129
+ role: 'user' | 'assistant' | 'system';
130
+ content: string;
131
+ parts?: MessagePart[];
132
+ timestamp: Date;
133
+ thinking?: string;
134
+ toolCalls?: Array<{
135
+ name: string;
136
+ args: any;
137
+ result?: any;
138
+ }>;
139
+ attachments?: any[];
140
+ metadata?: Record<string, unknown>;
141
+ }
142
+
143
+ export interface MessagePart {
144
+ type: 'text' | 'tool_call' | 'tool_result' | 'thinking' | 'image';
145
+ content: string;
146
+ toolName?: string;
147
+ toolArgs?: Record<string, unknown>;
148
+ toolResult?: unknown;
149
+ }
150
+
151
+ export interface Session {
152
+ id: string;
153
+ title?: string;
154
+ agentId: string;
155
+ workdir?: string;
156
+ messages: Message[];
157
+ createdAt: Date;
158
+ updatedAt: Date;
159
+ parentId?: string;
160
+ metadata?: Record<string, unknown>;
161
+ }
162
+
163
+ export interface Tool {
164
+ name: string;
165
+ description: string;
166
+ parameters: z.ZodType<any> | Record<string, unknown>;
167
+ execute: (args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>;
168
+ }
169
+
170
+ export interface ToolContext {
171
+ workdir: string;
172
+ session: Session;
173
+ signal?: AbortSignal;
174
+ }
175
+
176
+ export interface ToolResult {
177
+ success: boolean;
178
+ content: string;
179
+ error?: string;
180
+ metadata?: Record<string, unknown>;
181
+ }
182
+
183
+ export interface Plugin {
184
+ name: string;
185
+ version: string;
186
+ init?: (jarvis: JarvisInstance) => Promise<void>;
187
+ providers?: ProviderConfig[];
188
+ tools?: Tool[];
189
+ agents?: AgentConfig[];
190
+ }
191
+
192
+ export interface JarvisInstance {
193
+ config: JarvisConfig;
194
+ providers: Map<string, ProviderConfig>;
195
+ agents: Map<string, AgentConfig>;
196
+ tools: Map<string, Tool>;
197
+ sessions: Map<string, Session>;
198
+ activeSession?: Session;
199
+ activeAgent?: AgentConfig;
200
+ }
201
+
202
+ export type EventType =
203
+ | 'session.created'
204
+ | 'session.updated'
205
+ | 'session.deleted'
206
+ | 'message.created'
207
+ | 'message.updated'
208
+ | 'tool.started'
209
+ | 'tool.completed'
210
+ | 'agent.switched'
211
+ | 'error';
212
+
213
+ export interface JarvisEvent {
214
+ type: EventType;
215
+ timestamp: Date;
216
+ data: unknown;
217
+ }
@@ -0,0 +1,192 @@
1
+ import { app, BrowserWindow, ipcMain, dialog } from 'electron';
2
+ import { spawn, ChildProcess } from 'child_process';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ let mainWindow: BrowserWindow | null = null;
10
+ let bunServer: ChildProcess | null = null;
11
+ let serverPort: number = 5533;
12
+
13
+ /**
14
+ * Start Bun server in background
15
+ */
16
+ async function startBunServer(): Promise<string> {
17
+ return new Promise((resolve, reject) => {
18
+ console.log('[Electron] Starting Bun server...');
19
+
20
+ // Find project root (go up from dist/electron/main.js to project root)
21
+ const projectRoot = path.join(__dirname, '..', '..');
22
+ const cliPath = path.join(projectRoot, 'src', 'cli.ts');
23
+
24
+ // Start Bun server
25
+ bunServer = spawn('bun', ['run', cliPath, 'web', '--port', String(serverPort)], {
26
+ cwd: projectRoot,
27
+ stdio: 'pipe'
28
+ });
29
+
30
+ bunServer.stdout?.on('data', (data) => {
31
+ const output = data.toString();
32
+ console.log('[Bun Server]', output);
33
+
34
+ // Look for server ready message
35
+ if (output.includes('Server running') || output.includes('localhost')) {
36
+ resolve(`http://localhost:${serverPort}`);
37
+ }
38
+ });
39
+
40
+ bunServer.stderr?.on('data', (data) => {
41
+ console.error('[Bun Server Error]', data.toString());
42
+ });
43
+
44
+ bunServer.on('error', (error) => {
45
+ console.error('[Bun Server] Failed to start:', error);
46
+ reject(error);
47
+ });
48
+
49
+ // Timeout after 10 seconds
50
+ setTimeout(() => {
51
+ resolve(`http://localhost:${serverPort}`);
52
+ }, 3000);
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Create main Electron window
58
+ */
59
+ async function createWindow() {
60
+ console.log('[Electron] Creating main window...');
61
+
62
+ // Start Bun server first
63
+ const serverUrl = await startBunServer();
64
+ console.log('[Electron] Server URL:', serverUrl);
65
+
66
+ // Create browser window
67
+ mainWindow = new BrowserWindow({
68
+ width: 1400,
69
+ height: 900,
70
+ minWidth: 1024,
71
+ minHeight: 768,
72
+ title: 'JARVIS - AI Coding Assistant',
73
+ backgroundColor: '#09090b', // zinc-950
74
+ autoHideMenuBar: true, // Hide menu bar but allow Alt to show it
75
+ webPreferences: {
76
+ preload: path.join(__dirname, 'preload.js'),
77
+ contextIsolation: true,
78
+ nodeIntegration: false,
79
+ sandbox: false
80
+ },
81
+ show: false // Don't show until ready
82
+ });
83
+
84
+ // Show window when ready
85
+ mainWindow.once('ready-to-show', () => {
86
+ mainWindow?.show();
87
+ console.log('[Electron] Window ready');
88
+ });
89
+
90
+ // Load the Bun server URL
91
+ await mainWindow.loadURL(serverUrl);
92
+
93
+ // Open DevTools in development
94
+ if (process.env.NODE_ENV === 'development') {
95
+ mainWindow.webContents.openDevTools();
96
+ }
97
+
98
+ // Handle window closed
99
+ mainWindow.on('closed', () => {
100
+ mainWindow = null;
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Setup IPC handlers
106
+ */
107
+ function setupIPC() {
108
+ // Handle folder selection
109
+ ipcMain.handle('select-folder', async () => {
110
+ const result = await dialog.showOpenDialog({
111
+ properties: ['openDirectory'],
112
+ title: 'Select Workspace Folder'
113
+ });
114
+
115
+ if (result.canceled || result.filePaths.length === 0) {
116
+ return null;
117
+ }
118
+
119
+ return result.filePaths[0];
120
+ });
121
+
122
+ // Handle file selection
123
+ ipcMain.handle('select-files', async () => {
124
+ const result = await dialog.showOpenDialog({
125
+ properties: ['openFile', 'multiSelections'],
126
+ title: 'Select Files'
127
+ });
128
+
129
+ if (result.canceled) {
130
+ return [];
131
+ }
132
+
133
+ return result.filePaths;
134
+ });
135
+
136
+ // Get app version
137
+ ipcMain.handle('get-app-version', () => {
138
+ return app.getVersion();
139
+ });
140
+
141
+ // Get app mode
142
+ ipcMain.handle('get-app-mode', () => {
143
+ return 'electron';
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Cleanup on app quit
149
+ */
150
+ function cleanup() {
151
+ console.log('[Electron] Cleaning up...');
152
+
153
+ if (bunServer) {
154
+ console.log('[Electron] Stopping Bun server...');
155
+ bunServer.kill();
156
+ bunServer = null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * App lifecycle
162
+ */
163
+ app.whenReady().then(() => {
164
+ console.log('[Electron] App ready');
165
+ setupIPC();
166
+ createWindow();
167
+
168
+ // On macOS, re-create window when dock icon is clicked
169
+ app.on('activate', () => {
170
+ if (BrowserWindow.getAllWindows().length === 0) {
171
+ createWindow();
172
+ }
173
+ });
174
+ });
175
+
176
+ // Quit when all windows are closed (except on macOS)
177
+ app.on('window-all-closed', () => {
178
+ if (process.platform !== 'darwin') {
179
+ cleanup();
180
+ app.quit();
181
+ }
182
+ });
183
+
184
+ // Cleanup before quit
185
+ app.on('before-quit', () => {
186
+ cleanup();
187
+ });
188
+
189
+ // Handle uncaught errors
190
+ process.on('uncaughtException', (error) => {
191
+ console.error('[Electron] Uncaught exception:', error);
192
+ });
@@ -0,0 +1,25 @@
1
+ import { contextBridge, ipcRenderer } from 'electron';
2
+
3
+ /**
4
+ * Electron Preload Script
5
+ * Bridges main process and renderer process securely
6
+ */
7
+
8
+ // Expose safe APIs to renderer process
9
+ contextBridge.exposeInMainWorld('electron', {
10
+ // Folder selection
11
+ selectFolder: () => ipcRenderer.invoke('select-folder'),
12
+
13
+ // File selection
14
+ selectFiles: () => ipcRenderer.invoke('select-files'),
15
+
16
+ // App info
17
+ getAppVersion: () => ipcRenderer.invoke('get-app-version'),
18
+ getAppMode: () => ipcRenderer.invoke('get-app-mode'),
19
+
20
+ // Environment detection
21
+ isElectron: true,
22
+ platform: process.platform
23
+ });
24
+
25
+ console.log('[Preload] Electron APIs exposed to renderer');
@@ -0,0 +1,20 @@
1
+ /**
2
+ * TypeScript type definitions for Electron APIs exposed to renderer
3
+ */
4
+
5
+ export interface ElectronAPI {
6
+ selectFolder: () => Promise<string | null>;
7
+ selectFiles: () => Promise<string[]>;
8
+ getAppVersion: () => Promise<string>;
9
+ getAppMode: () => Promise<'electron' | 'web'>;
10
+ isElectron: boolean;
11
+ platform: NodeJS.Platform;
12
+ }
13
+
14
+ declare global {
15
+ interface Window {
16
+ electron?: ElectronAPI;
17
+ }
18
+ }
19
+
20
+ export {};
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ // Jarvis AI - Main Entry Point
2
+ // This file exports all modules for SDK/library usage
3
+
4
+ export * from './core/types.js';
5
+ export * from './config/index.js';
6
+ export * from './providers/index.js';
7
+ export * from './agents/index.js';
8
+ export * from './tools/index.js';
9
+ export * from './sessions/index.js';
10
+ export * from './core/executor.js';
11
+ export * from './core/plugins.js';
12
+ export * from './utils/index.js';