quotalala 0.0.0-stage β†’ 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quotalala Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,176 @@
1
- # Temporary Holding Version
1
+ # Quotalala πŸŽ›οΈ
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Quotalala** is a local desktop application built with Electron that automatically distributes a single prompt across Claude, ChatGPT (Codex), and Google Gemini subscription quotas without wasting usage limits. Claude serves as the main coordinator and synthesizer.
4
+
5
+ Launch Quotalala from the project you are working on. The npm launcher opens the desktop app with that folder as the working directory for the agents, and its Quotalala conversations and project memory load automatically. Write your prompt and continue working in that project.
6
+
7
+ ---
8
+
9
+ ## Key Principles & Guarantees
10
+
11
+ - **No API Keys**: Only invokes installed and authenticated command-line tools on your machine (`claude -p`, `codex exec`, `agy`), with prompts sent through stdin.
12
+ - **Zero Credential Scraping**: Never reads, copies, or proxies user auth tokens or session secrets.
13
+ - **Secure Architecture**: Electron desktop app with `contextIsolation: true`, `nodeIntegration: false`, and sandboxed IPC communication.
14
+ - **Windows-Optimized**: Native handling of `.cmd`, `.bat`, and `.exe` binaries, piping prompts through `stdin` to eliminate command-line character escaping issues.
15
+ - **System Tray & Desktop Notifications**: Minimizes to system tray on close with real-time quota status on hover and Windows reminder notifications when quotas are approaching renewal.
16
+
17
+ ---
18
+
19
+ ## Architecture & Project Structure
20
+
21
+ ```
22
+ quotalala/
23
+ β”œβ”€β”€ src/
24
+ β”‚ β”œβ”€β”€ main/ # Electron main process
25
+ β”‚ β”‚ β”œβ”€β”€ main.js # Window, system tray, native notifications
26
+ β”‚ β”‚ β”œβ”€β”€ preload.js # Secure contextBridge IPC
27
+ β”‚ β”‚ β”œβ”€β”€ coordinator.js # Claude task breakdown & synthesis
28
+ β”‚ β”‚ β”œβ”€β”€ quota.js # Persistent quota & usage tracking
29
+ β”‚ β”‚ β”œβ”€β”€ cli-locator.js # Cross-platform CLI binary resolution
30
+ β”‚ β”‚ └── agents/ # Modular agent plugins
31
+ β”‚ β”‚ β”œβ”€β”€ base-agent.js # Base agent class
32
+ β”‚ β”‚ β”œβ”€β”€ claude.js # Claude Code CLI module
33
+ β”‚ β”‚ β”œβ”€β”€ codex.js # Codex / ChatGPT CLI module
34
+ β”‚ β”‚ └── gemini.js # Google Gemini CLI module
35
+ β”‚ └── renderer/ # Desktop UI (Single Screen)
36
+ β”‚ β”œβ”€β”€ index.html # Clean HTML layout
37
+ β”‚ β”œβ”€β”€ style.css # Modern dark-mode styling
38
+ β”‚ β”œβ”€β”€ app.js # IPC client logic & live streams
39
+ β”‚ └── i18n/ # Multi-language translations
40
+ β”‚ β”œβ”€β”€ tr.json # Turkish (default)
41
+ β”‚ └── en.json # English
42
+ β”œβ”€β”€ assets/ # Application icons (.ico, .png)
43
+ β”œβ”€β”€ test/ # Diagnostics & tests
44
+ β”œβ”€β”€ AGENTS.md # Agent system guidelines
45
+ β”œβ”€β”€ LICENSE # MIT License
46
+ └── package.json
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Getting Started
52
+
53
+ ### Prerequisites
54
+
55
+ - Node.js 22.12+ for development (tested on Node v24). The installed desktop application includes its own runtime.
56
+ - Local AI CLI tools installed and logged in:
57
+ - **Claude Code**: `@anthropic-ai/claude-code` (`claude auth status`)
58
+ - **Codex CLI**: `codex-cli` (`codex login status`)
59
+ - **Antigravity CLI (Gemini)**: `agy` (optional, disabled by default; sign in interactively)
60
+
61
+ > [!WARNING]
62
+ > **Gemini / Antigravity CLI Notice**: Some accounts using Antigravity CLI automation have reportedly been restricted by Google. Gemini starts disabled and requires explicit acceptance of the risk notice on its card. Its stdin and optional effort capabilities are checked against the installed CLI's help; unverified flags are never used.
63
+
64
+ ### Use Quotalala in your project
65
+
66
+ The Windows npm launcher opens the desktop app directly in your project:
67
+
68
+ ```powershell
69
+ # Run from your project's terminal.
70
+ npx quotalala
71
+
72
+ # Or install once, then use the short command in any project.
73
+ npm install -g quotalala
74
+ quotalala
75
+ ```
76
+
77
+ The npm launcher always uses your terminal's current folder, including folders without project markers. Its sidebar loads that project's saved conversations, and its memory panel loads the project's summary. Every new task runs in that folder. You can also use `quotalala --project "C:\projects\my-app"` or `quotalala "C:\projects\my-app"` to open another folder.
78
+
79
+ The npm package requires Node.js 22.12+ and downloads its pinned Electron runtime during dependency installation. It does not require a separate Quotalala installer or native compilation. The first installation/download can take some time; normal launches use the installed runtime. It opens the Electron desktop interface.
80
+
81
+ #### Standalone Windows installer
82
+
83
+ The NSIS installer remains an alternative without a Node.js requirement. Launch its executable from your project's terminal, replacing the example with the actual installed path:
84
+
85
+ ```powershell
86
+ & "C:\path\to\Quotalala.exe"
87
+
88
+ # Explicit project, useful when launching from another directory:
89
+ & "C:\path\to\Quotalala.exe" --project "C:\projects\my-app"
90
+ ```
91
+
92
+ The standalone executable detects the current folder when it contains `.git`, `AGENTS.md`, `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod` or `README.md`. Launching it without a detected or explicit project restores the last project. The sidebar's folder picker is available to switch projects, or to open one when no previous project exists.
93
+
94
+ ### Develop Quotalala itself
95
+
96
+ Run these commands in the cloned **Quotalala repository**:
97
+
98
+ ```bash
99
+ # Install dependencies
100
+ npm ci
101
+
102
+ # Run desktop application
103
+ npm start
104
+ ```
105
+
106
+ To test the development application against another project, use `npm start -- --project "C:\projects\my-app"` from the Quotalala repository.
107
+
108
+ ### Packaging Windows Installer
109
+
110
+ ```bash
111
+ # Builds NSIS .exe installer in release/
112
+ npm run build
113
+ ```
114
+
115
+ ### Packaging the npm launcher
116
+
117
+ ```powershell
118
+ # From the Quotalala repository; validates an explicit package file list.
119
+ npm run pack:cli
120
+
121
+ # Local installation for testing before npm publication (changes your global npm installation):
122
+ npm install -g .\.test-artifacts\npm\quotalala-1.0.0.tgz
123
+
124
+ # Then, from the project you want to work on:
125
+ quotalala
126
+ ```
127
+
128
+ The generated archive includes the launcher, application source/assets, README and license. Its only runtime dependency is the pinned Electron package; development/build tools and user data are excluded. The root development manifest remains separate so the NSIS build continues using Electron as a development dependency. Packaging does not publish to npm.
129
+
130
+ ---
131
+
132
+ ## History and project memory
133
+
134
+ The project detected at launch controls all task working directories, conversation history and project memory. Quotalala remembers the last project and lists recent folders for switching between projects. Its workspace preference is stored locally in `~/.quotalala/workspace.json`, independently of whether conversation saving is enabled.
135
+
136
+ The application checks that the active directory is readable before spending CLI quota; it does not bypass the CLI's own sandbox or policy. The sidebar shows Quotalala conversations saved for that folder. Native Claude/Codex/Gemini transcripts are not imported into this history.
137
+
138
+ The desktop layout has a conversation sidebar, compact agent cards, a central task composer and a project-memory panel. Outputs, activity timeline and diagnostic logs have separate tabs. `Ctrl+N` opens a new conversation and `Ctrl+Enter` sends a task from the composer.
139
+
140
+ Each coordinated run records its prompt, plan, agent/model choices, outputs, structured logs and status. Open, rename, delete or continue sessions from the project-grouped sidebar. Continuing uses an exact native CLI session ID when supported; if that session is unavailable, project memory provides the context. Resume support is verified using at most two help commands per CLI per application run. Claude and Codex support was verified locally; `agy` was unavailable on the development machine.
141
+
142
+ Data stays in `~/.quotalala/sessions/` and `~/.quotalala/memory/` as JSON. Claude updates a bounded 2000-character project summary after each saved run, using the light model tier. You can edit this summary, reset a project's memory, turn off saving, or delete all sessions and memories in Settings. Disabling saving also skips automatic memory generation; explicitly saving an edited summary still stores it. CLI tools may independently retain their own transcripts.
143
+
144
+ Recognizable API keys, bearer tokens, JWTs and labeled secrets are masked before Quotalala writes records. Redaction is pattern based and cannot recognize every possible unlabeled secret; review raw diagnostics before sharing them. Quota error history remains capped at 200 characters per entry, independently of full session output.
145
+
146
+ Logs support severity and agent filters, text selection, Ctrl+C, a native Copy/Select All context menu, row copy controls and Copy All. Error cards provide a localized explanation, suggested fix and copyable raw output. Task outputs have the same selection/copy controls. All generated content is inserted as text rather than executable HTML.
147
+
148
+ Models come from `~/.quotalala/models.json`; Settings exposes the list with required source metadata. The planner selects a tier and the backend resolves it to a model. Preview models are excluded from default choices. An unavailable model falls back to the CLI default with a warning. Automatic model catalog discovery is still pending.
149
+
150
+ Gemini has no bundled model names because they could not be verified on the development machine. Add a model with an actual CLI/local-source reference after verifying your `agy` installation, or use its default model. Invalid local model catalogs fall back to the validated bundled list without replacing your file.
151
+
152
+ Sessions also retain a structured activity timeline: planning, assignments with model/tier/reason, completion or failure, output transfers, quota-driven agent changes and final review. Expand an event to inspect and copy its input/output; related assignment and completion records are linked without duplicating payloads. Events appear live and are saved immediately with the existing secret masking and history preferences. Older sessions without activity records show an empty timeline notice.
153
+
154
+ Planning receives project memory plus at most 20 short activity headings from that project. When continuing a session, its own timeline headings take priority within that same 20-event budget. Complete historical event inputs and outputs are never included in this activity context.
155
+
156
+ ## Validation
157
+
158
+ Task completion is evaluated separately from a successful CLI exit. Codex command failures are retained alongside the final answer. Project review tasks require file/line references and, when command telemetry is available, a successful file-read command. A policy-denied task is shown as blocked and does not increase the completed-job counter. Remaining file-review tasks are skipped after a shared access denial; Quotalala does not automatically route around the policy with another agent or disable CLI protections. This detects common failure reports and command evidence; it is not a proof that every finding is correct.
159
+
160
+ ```bash
161
+ npm test # Offline unit/integration tests, mocked CLIs
162
+ npm run test:ui # Electron UI tests, mocked CLIs and local fixture storage
163
+ npm run test:startup # Production IPC startup with local fixtures
164
+ npm run check # Git candidate files, syntax, translations, lockfile and secret patterns
165
+ npm run check:package -- path/to/app.asar # Compare a built package with current source
166
+ ```
167
+
168
+ Fixtures and screenshots stay in `.test-artifacts/`, which is ignored by Git. The opt-in `node test/e2e-real.js --approved` performs one short real coordinated flow and writes to the normal local store. Run it only after permission to use CLI quota and create records outside the repository. Do not run live diagnostics automatically on startup.
169
+
170
+ ## Release preparation
171
+
172
+ Windows source checks and mocked Electron tests are configured in `.github/workflows/checks.yml`. A manual Actions run also builds and verifies an unsigned installer for download, without publishing a Release. See [RELEASE-CHECKLIST.md](RELEASE-CHECKLIST.md) for verified checks, known build dependency advisories and the remaining release steps.
173
+
174
+ ## License
175
+
176
+ MIT Β© Quotalala Contributors
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
2
+ <rect width="512" height="512" rx="112" fill="#bdd4b9"/>
3
+ <path fill="#223227" fill-rule="evenodd" d="M252 126c-71 0-117 51-117 121s44 119 106 119c24 0 44-7 59-21v87h56V132h-56v16c-13-14-29-22-48-22zm-3 52c32 0 52 26 52 68 0 43-21 69-52 69s-54-25-54-69c0-42 21-68 54-68z"/>
4
+ <circle cx="409" cy="337" r="27" fill="#223227"/>
5
+ </svg>
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "quotalala",
3
+ "version": "1.0.0",
4
+ "main": "src/main/main.js",
5
+ "productName": "Quotalala",
6
+ "license": "MIT"
7
+ }
@@ -0,0 +1,118 @@
1
+ const { runWithStdin } = require('../cli-locator');
2
+
3
+ class BaseAgent {
4
+ constructor(options) {
5
+ this.id = options.id;
6
+ this.name = options.name;
7
+ this.provider = options.provider;
8
+ this.role = options.role;
9
+ this.command = options.command;
10
+ }
11
+
12
+ getRunArgs() {
13
+ throw new Error('getRunArgs must be implemented by subclass');
14
+ }
15
+
16
+ async getVersion() {
17
+ const res = await this.execute(['--version'], '', { timeout: 10000 });
18
+ if (res.code === 0 && res.stdout) {
19
+ return res.stdout.split(/\r?\n/)[0].trim();
20
+ }
21
+ return null;
22
+ }
23
+
24
+ async checkAuth() {
25
+ return { loggedIn: false };
26
+ }
27
+
28
+ async runHealthTest(onLog = () => {}, options = {}) {
29
+ onLog(`[${this.id.toUpperCase()}] Sağlık testi başlatılıyor...`);
30
+
31
+ const version = await this.getVersion();
32
+ onLog(`[${this.id.toUpperCase()}] CLI SΓΌrΓΌmΓΌ: ${version || 'BulunamadΔ±'}`);
33
+
34
+ const authInfo = await this.checkAuth();
35
+ if (authInfo.loggedIn) {
36
+ onLog(`[${this.id.toUpperCase()}] Oturum: Aktif (${authInfo.details || authInfo.subscription || ''})`);
37
+ }
38
+
39
+ onLog(`[${this.id.toUpperCase()}] stdin ΓΌzerinden test promptu gΓΆnderiliyor: "OK yaz"`);
40
+ let res = await this.runPrompt('Sadece tek kelime OK yaz', { ...options, timeout: 35000, healthTest: true });
41
+ let modelFallback = false;
42
+ if (res.code !== 0 && options.model && /model.*(?:not found|not available|not supported|access|invalid|unknown)|unknown.*model/i.test(res.stderr + res.stdout)) {
43
+ modelFallback = true;
44
+ res = await this.runPrompt('Sadece tek kelime OK yaz', { ...options, model: undefined, thinking: undefined, timeout: 35000, healthTest: true });
45
+ }
46
+
47
+ const durationSec = (res.durationMs / 1000).toFixed(2);
48
+ const combinedOutput = (res.stdout + '\n' + res.stderr).trim();
49
+ const containsOk = /^OK[.!]?$/i.test(res.stdout.trim());
50
+
51
+ let status = 'error';
52
+ let message = '';
53
+ let solution = null;
54
+
55
+ if (res.code === 0 && containsOk) {
56
+ status = 'healthy';
57
+ if (!authInfo.loggedIn) {
58
+ authInfo.loggedIn = true;
59
+ authInfo.details = 'Oturum aΓ§Δ±k';
60
+ }
61
+ message = `Başarılı ("OK" yanıtı alındı, ${durationSec}s)`;
62
+ onLog(`[${this.id.toUpperCase()}] βœ“ BaşarΔ±lΔ±! YanΔ±t: "${res.stdout.split('\n')[0].trim()}" (${durationSec}s)`);
63
+ } else if (res.code === -999) {
64
+ status = 'timeout';
65
+ message = 'Zaman aşımı (CLI yanıt vermedi)';
66
+ onLog(`[${this.id.toUpperCase()}] βœ— Zaman aşımΔ± gerΓ§ekleşti.`);
67
+ } else if (
68
+ combinedOutput.includes('Auth method') ||
69
+ combinedOutput.includes('GEMINI_API_KEY') ||
70
+ combinedOutput.includes('auth.json') ||
71
+ combinedOutput.includes('login')
72
+ ) {
73
+ status = 'auth_required';
74
+ message = 'Giriş yapılmamış (Oturum açılması gerekiyor)';
75
+ solution = this.id === 'gemini'
76
+ ? 'Terminalde bir kez `agy` komutunu çalıştırarak Google hesabınızla giriş yapınız.'
77
+ : 'CLI ΓΌzerinde oturum aΓ§Δ±nΔ±z.';
78
+ onLog(`[${this.id.toUpperCase()}] ! Giriş gerekli: ${message}`);
79
+ } else if (res.code === -1 && res.stderr.includes('spawn')) {
80
+ status = 'missing';
81
+ message = 'CLI kurulu değil veya PATH üzerinde bulunamadı';
82
+ solution = this.id === 'gemini'
83
+ ? 'Antigravity CLI (agy) kurulumunu yapΔ±nΔ±z.'
84
+ : 'Δ°lgili CLI aracΔ±nΔ± kurunuz.';
85
+ onLog(`[${this.id.toUpperCase()}] βœ— CLI kurulu değil.`);
86
+ } else {
87
+ status = 'failed';
88
+ message = `Hata kodu ${res.code}: ${res.stderr || res.stdout || 'Bilinmeyen hata'}`;
89
+ onLog(`[${this.id.toUpperCase()}] βœ— Hata oluştu: ${message}`);
90
+ }
91
+
92
+ return {
93
+ id: this.id,
94
+ name: this.name,
95
+ version,
96
+ modelFallback,
97
+ authInfo,
98
+ status,
99
+ healthy: status === 'healthy',
100
+ message,
101
+ solution,
102
+ durationMs: res.durationMs,
103
+ rawStdout: res.stdout,
104
+ rawStderr: res.stderr,
105
+ timestamp: new Date().toISOString()
106
+ };
107
+ }
108
+
109
+ async runPrompt(prompt, options = {}) {
110
+ return this.execute(this.getRunArgs(prompt, options), prompt + '\n', options);
111
+ }
112
+
113
+ execute(args, input = '', options = {}) {
114
+ return runWithStdin(this.command, args, input, options);
115
+ }
116
+ }
117
+
118
+ module.exports = BaseAgent;
@@ -0,0 +1,52 @@
1
+ const BaseAgent = require('./base-agent');
2
+
3
+ class ClaudeAgent extends BaseAgent {
4
+ constructor() {
5
+ super({
6
+ id: 'claude',
7
+ name: 'Claude Code',
8
+ provider: 'Anthropic',
9
+ role: 'KoordinatΓΆr & PlanlayΔ±cΔ±',
10
+ command: 'claude'
11
+ });
12
+ }
13
+
14
+ getRunArgs(prompt, options = {}) {
15
+ const args = ['-p', '--output-format', options.healthTest ? 'text' : 'json'];
16
+ if (options.model) args.push('--model', options.model);
17
+ if (options.thinking) args.push('--effort', options.thinking);
18
+ if (options.resumeId && this.capabilities?.resume) args.push('--resume', options.resumeId);
19
+ if (options.noTools && this.capabilities?.noTools) args.push('--tools', '');
20
+ return args;
21
+ }
22
+
23
+ async runPrompt(prompt, options = {}) {
24
+ const res = await super.runPrompt(prompt, options);
25
+ if (!options.healthTest && res.code === 0) {
26
+ try {
27
+ const data = JSON.parse(res.stdout);
28
+ res.stdout = data.result || '';
29
+ res.sessionId = data.session_id;
30
+ if (data.is_error) { res.code = 1; res.stderr = res.stdout; }
31
+ } catch { res.code = 1; res.stderr = 'Invalid Claude JSON response: ' + res.stdout; }
32
+ }
33
+ return res;
34
+ }
35
+
36
+ async checkAuth() {
37
+ try {
38
+ const res = await this.execute(['auth', 'status'], '', { timeout: 10000 });
39
+ const parsed = JSON.parse(res.stdout.trim());
40
+ return {
41
+ loggedIn: !!parsed.loggedIn,
42
+ subscription: parsed.subscriptionType || 'Pro',
43
+ email: parsed.email || null,
44
+ details: parsed.subscriptionType ? `${parsed.subscriptionType.toUpperCase()} Abonelik` : 'Giriş Yapıldı'
45
+ };
46
+ } catch {
47
+ return { loggedIn: false };
48
+ }
49
+ }
50
+ }
51
+
52
+ module.exports = new ClaudeAgent();
@@ -0,0 +1,66 @@
1
+ const BaseAgent = require('./base-agent');
2
+ const { policyPattern } = require('../task-outcome');
3
+
4
+ class CodexAgent extends BaseAgent {
5
+ constructor() {
6
+ super({
7
+ id: 'codex',
8
+ name: 'ChatGPT / Codex',
9
+ provider: 'OpenAI',
10
+ role: 'Kodlama ve GΓΆrev YΓΌrΓΌtme',
11
+ command: 'codex'
12
+ });
13
+ }
14
+
15
+ getRunArgs(prompt, options = {}) {
16
+ const args = ['exec'];
17
+ if (options.resumeId && this.capabilities?.resume) args.push('resume', options.resumeId);
18
+ args.push('--ignore-user-config', '--skip-git-repo-check');
19
+ if (!options.healthTest) args.push('--json');
20
+ if (options.model) args.push('--model', options.model);
21
+ if (options.thinking) args.push('-c', `model_reasoning_effort=${options.thinking}`);
22
+ args.push('-');
23
+ return args;
24
+ }
25
+
26
+ async runPrompt(prompt, options = {}) {
27
+ const res = await super.runPrompt(prompt, options);
28
+ if (!options.healthTest) {
29
+ const messages = [];
30
+ res.toolFailures = [];
31
+ res.successfulFileCommands = 0;
32
+ res.toolChecksAvailable = true;
33
+ for (const line of res.stdout.split(/\r?\n/)) {
34
+ try {
35
+ const event = JSON.parse(line);
36
+ if (event.type === 'thread.started') res.sessionId = event.thread_id;
37
+ if (event.type === 'item.completed' && event.item?.type === 'agent_message') messages.push(event.item.text);
38
+ if (event.type === 'item.completed' && event.item?.type === 'command_execution') {
39
+ const item = event.item, output = item.aggregated_output || item.output || '';
40
+ if (typeof item.exit_code === 'number' && item.exit_code !== 0 || item.status === 'failed' || policyPattern.test(output)) res.toolFailures.push({ command: item.command, output, exitCode: item.exit_code, policyBlocked: policyPattern.test(output) });
41
+ else if ((item.exit_code === 0 || item.status === 'completed') && /rg|Get-Content|Get-ChildItem|\bcat\b|\bls\b|read.*file/i.test(item.command || '')) res.successfulFileCommands++;
42
+ }
43
+ if (event.type === 'turn.failed' || event.type === 'error') { res.code = res.code || 1; res.stderr += '\n' + (event.error?.message || event.message || line); }
44
+ } catch { /* Ignore non-JSON diagnostics; never treat tool output as final text. */ }
45
+ }
46
+ res.stdout = messages.at(-1) || '';
47
+ }
48
+ return res;
49
+ }
50
+
51
+ async checkAuth() {
52
+ try {
53
+ const res = await this.execute(['login', 'status'], '', { timeout: 10000 });
54
+ const text = (res.stdout + '\n' + res.stderr).trim();
55
+ const loggedIn = text.toLowerCase().includes('logged in');
56
+ return {
57
+ loggedIn,
58
+ details: text || 'ChatGPT Oturumu'
59
+ };
60
+ } catch {
61
+ return { loggedIn: false };
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = new CodexAgent();
@@ -0,0 +1,42 @@
1
+ const BaseAgent = require('./base-agent');
2
+
3
+ class GeminiAgent extends BaseAgent {
4
+ constructor() {
5
+ super({
6
+ id: 'gemini',
7
+ name: 'Google Gemini',
8
+ provider: 'Google',
9
+ role: 'Hızlı Yanıt & Araştırma',
10
+ command: 'agy',
11
+ enabled: false,
12
+ warning: "Google, Antigravity CLI'ın otomatik kullanımına karşı bazı hesapları kısıtladığı rapor edildi. Bu ajanı açarak Google hesabınla ilgili riski kabul etmiş olursun."
13
+ });
14
+ }
15
+
16
+ getRunArgs(prompt = 'Sadece tek kelime OK yaz', options = {}) {
17
+ if (typeof prompt === 'object' && prompt !== null) {
18
+ options = prompt;
19
+ prompt = 'Sadece tek kelime OK yaz';
20
+ }
21
+ // No prompt argument and no unverified stdin/resume flags.
22
+ if (this.capabilities && !this.capabilities.available) throw new Error(this.capabilities.error || 'agy CLI not found.');
23
+ if (!this.capabilities?.stdin) throw new Error('agy stdin support could not be verified from --help.');
24
+ const args = this.capabilities.print ? ['-p'] : [];
25
+ if (options && options.model) {
26
+ if (!this.capabilities.model) throw new Error('Model not supported by agy --help.');
27
+ args.push('--model', options.model);
28
+ }
29
+ if (options && options.thinking && this.capabilities.effort) {
30
+ args.push('--effort', options.thinking);
31
+ }
32
+ return args;
33
+ }
34
+
35
+ async checkAuth() {
36
+ // Antigravity CLI does not have a separate auth status command.
37
+ // Auth status is validated by executing headless prompts.
38
+ return { loggedIn: false };
39
+ }
40
+ }
41
+
42
+ module.exports = new GeminiAgent();
@@ -0,0 +1,24 @@
1
+ const claude = require('./claude');
2
+ const codex = require('./codex');
3
+ const gemini = require('./gemini');
4
+
5
+ const agents = {
6
+ claude,
7
+ codex,
8
+ gemini
9
+ };
10
+
11
+ function getAgent(id) {
12
+ const key = id.toLowerCase();
13
+ return agents[key] || null;
14
+ }
15
+
16
+ function getAllAgents() {
17
+ return Object.values(agents);
18
+ }
19
+
20
+ module.exports = {
21
+ agents,
22
+ getAgent,
23
+ getAllAgents
24
+ };
@@ -0,0 +1,23 @@
1
+ // At most two read-only help calls per CLI, cached for this application run.
2
+ async function discover(agent) {
3
+ if (agent.capabilities) return agent.capabilities;
4
+ const args = agent.id === 'codex' ? ['exec', '--help'] : ['--help'];
5
+ const res = await agent.execute(args, '', { timeout: 10000 });
6
+ const help = res.code === 0 ? res.stdout : '';
7
+ const capabilities = { resume: false, stdin: agent.id !== 'gemini', source: `${agent.command} ${args.join(' ')}`, available: res.code === 0, error: res.code === 0 ? null : (res.stderr || 'CLI help unavailable') };
8
+ if (agent.id === 'claude') {
9
+ capabilities.resume = /--resume\s/.test(help) && /--output-format/.test(help);
10
+ capabilities.noTools = /--tools\s/.test(help);
11
+ } else if (agent.id === 'codex' && /\bresume\s/.test(help)) {
12
+ const resume = await agent.execute(['exec', 'resume', '--help'], '', { timeout: 10000 });
13
+ capabilities.resume = resume.code === 0 && /SESSION_ID/.test(resume.stdout) && /stdin/.test(resume.stdout);
14
+ } else if (agent.id === 'gemini') {
15
+ capabilities.stdin = /stdin|standard input/i.test(help);
16
+ capabilities.print = /-p,?\s+--print/.test(help);
17
+ capabilities.effort = /--effort\s/.test(help);
18
+ capabilities.model = /--model\s/.test(help);
19
+ }
20
+ agent.capabilities = capabilities;
21
+ return capabilities;
22
+ }
23
+ module.exports = { discover };