add-skill-kit 3.2.3 → 3.2.5

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 (126) hide show
  1. package/README.md +1 -1
  2. package/bin/lib/commands/help.js +0 -4
  3. package/bin/lib/commands/install.js +90 -9
  4. package/bin/lib/ui.js +1 -1
  5. package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
  6. package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
  7. package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
  8. package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
  9. package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
  10. package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
  11. package/lib/agent-cli/bin/agent.js +191 -0
  12. package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
  13. package/lib/agent-cli/dashboard/index.html +538 -0
  14. package/lib/agent-cli/lib/audit.js +154 -0
  15. package/lib/agent-cli/lib/audit.test.js +100 -0
  16. package/lib/agent-cli/lib/auto-learn.js +319 -0
  17. package/lib/agent-cli/lib/auto_preview.py +148 -0
  18. package/lib/agent-cli/lib/backup.js +138 -0
  19. package/lib/agent-cli/lib/backup.test.js +78 -0
  20. package/lib/agent-cli/lib/checklist.py +222 -0
  21. package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
  22. package/lib/agent-cli/lib/completion.js +149 -0
  23. package/lib/agent-cli/lib/config.js +35 -0
  24. package/lib/agent-cli/lib/eslint-fix.js +238 -0
  25. package/lib/agent-cli/lib/evolution-signal.js +215 -0
  26. package/lib/agent-cli/lib/export.js +86 -0
  27. package/lib/agent-cli/lib/export.test.js +65 -0
  28. package/lib/agent-cli/lib/fix.js +337 -0
  29. package/lib/agent-cli/lib/fix.test.js +80 -0
  30. package/lib/agent-cli/lib/gemini-export.js +83 -0
  31. package/lib/agent-cli/lib/generate-registry.js +42 -0
  32. package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
  33. package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
  34. package/lib/agent-cli/lib/ignore.js +116 -0
  35. package/lib/agent-cli/lib/ignore.test.js +58 -0
  36. package/lib/agent-cli/lib/init.js +124 -0
  37. package/lib/agent-cli/lib/learn.js +255 -0
  38. package/lib/agent-cli/lib/learn.test.js +70 -0
  39. package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
  40. package/lib/agent-cli/lib/proposals.js +199 -0
  41. package/lib/agent-cli/lib/proposals.test.js +56 -0
  42. package/lib/agent-cli/lib/recall.js +820 -0
  43. package/lib/agent-cli/lib/recall.test.js +107 -0
  44. package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
  45. package/lib/agent-cli/lib/session_manager.py +120 -0
  46. package/lib/agent-cli/lib/settings.js +227 -0
  47. package/lib/agent-cli/lib/skill-learn.js +296 -0
  48. package/lib/agent-cli/lib/stats.js +132 -0
  49. package/lib/agent-cli/lib/stats.test.js +94 -0
  50. package/lib/agent-cli/lib/types.js +33 -0
  51. package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
  52. package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
  53. package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
  54. package/lib/agent-cli/lib/ui/common.js +83 -0
  55. package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
  56. package/lib/agent-cli/lib/ui/custom-select.js +69 -0
  57. package/lib/agent-cli/lib/ui/dashboard-ui.js +222 -0
  58. package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
  59. package/lib/agent-cli/lib/ui/export-ui.js +94 -0
  60. package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
  61. package/lib/agent-cli/lib/ui/help-ui.js +49 -0
  62. package/lib/agent-cli/lib/ui/index.js +199 -0
  63. package/lib/agent-cli/lib/ui/init-ui.js +56 -0
  64. package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
  65. package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
  66. package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
  67. package/lib/agent-cli/lib/ui/pretty.js +145 -0
  68. package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
  69. package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
  70. package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
  71. package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
  72. package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
  73. package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
  74. package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
  75. package/lib/agent-cli/lib/verify_all.py +327 -0
  76. package/lib/agent-cli/lib/watcher.js +181 -0
  77. package/lib/agent-cli/lib/watcher.test.js +85 -0
  78. package/lib/agent-cli/package.json +51 -0
  79. package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
  80. package/lib/agent-cli/scripts/dashboard_server.js +224 -0
  81. package/lib/agent-cli/scripts/error_sensor.js +565 -0
  82. package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
  83. package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
  84. package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
  85. package/lib/agent-cli/scripts/rule_sharing.js +374 -0
  86. package/lib/agent-cli/scripts/skill_injector.js +387 -0
  87. package/lib/agent-cli/scripts/success_sensor.js +500 -0
  88. package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
  89. package/lib/agent-cli/services/auto-learn-service.js +247 -0
  90. package/lib/agent-cli/src/MIGRATION.md +418 -0
  91. package/lib/agent-cli/src/README.md +367 -0
  92. package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
  93. package/lib/agent-cli/src/core/evolution/index.js +17 -0
  94. package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
  95. package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
  96. package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
  97. package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
  98. package/lib/agent-cli/src/core/index.js +15 -0
  99. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
  100. package/lib/agent-cli/src/core/learning/index.js +12 -0
  101. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
  102. package/lib/agent-cli/src/core/scanning/index.js +14 -0
  103. package/lib/agent-cli/src/data/index.js +13 -0
  104. package/lib/agent-cli/src/data/repositories/index.js +8 -0
  105. package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
  106. package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
  107. package/lib/agent-cli/src/data/storage/index.js +8 -0
  108. package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
  109. package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
  110. package/lib/agent-cli/src/infrastructure/index.js +13 -0
  111. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
  112. package/lib/agent-cli/src/services/export-service.js +162 -0
  113. package/lib/agent-cli/src/services/index.js +13 -0
  114. package/lib/agent-cli/src/services/learning-service.js +99 -0
  115. package/lib/agent-cli/types/index.d.ts +343 -0
  116. package/lib/agent-cli/utils/benchmark.js +269 -0
  117. package/lib/agent-cli/utils/logger.js +303 -0
  118. package/lib/agent-cli/utils/ml_patterns.js +300 -0
  119. package/lib/agent-cli/utils/recovery.js +312 -0
  120. package/lib/agent-cli/utils/telemetry.js +290 -0
  121. package/lib/agentskillskit-cli/README.md +21 -0
  122. package/{node_modules/agentskillskit-cli/bin → lib/agentskillskit-cli}/ag-smart.js +15 -15
  123. package/lib/agentskillskit-cli/package.json +51 -0
  124. package/package.json +19 -9
  125. /package/bin/{cli.js → kit.js} +0 -0
  126. /package/{node_modules/agentskillskit-cli → lib/agent-cli}/README.md +0 -0
@@ -0,0 +1,325 @@
1
+ /**
2
+ * @fileoverview Intelligent Routing UI for Agent Skill Kit
3
+ * FAANG-level professional agent display with Clack UI
4
+ */
5
+
6
+ import * as p from "@clack/prompts";
7
+ import pc from "picocolors";
8
+
9
+ // ============================================================================
10
+ // CLACK SYMBOLS
11
+ // ============================================================================
12
+
13
+ const S = {
14
+ BAR: "│",
15
+ BAR_END: "└",
16
+ CORNER_TOP_RIGHT: "╮",
17
+ CORNER_BOTTOM_RIGHT: "╯",
18
+ STEP_ACTIVE: "◆",
19
+ STEP_SUBMIT: "◇",
20
+ RADIO_ACTIVE: "●",
21
+ RADIO_INACTIVE: "○",
22
+ CONNECT_LEFT: "├",
23
+ H_LINE: "─"
24
+ };
25
+
26
+ // ============================================================================
27
+ // AGENT BRANDING
28
+ // ============================================================================
29
+
30
+ const AGENT_EMOJI = "🤖";
31
+
32
+ const COMPLEXITY_CONFIG = {
33
+ "SIMPLE": {
34
+ color: pc.green,
35
+ icon: S.RADIO_ACTIVE,
36
+ label: "Focused",
37
+ description: "Single specialist engaged"
38
+ },
39
+ "MODERATE": {
40
+ color: pc.yellow,
41
+ icon: `${S.RADIO_ACTIVE}${S.RADIO_ACTIVE}`,
42
+ label: "Collaborative",
43
+ description: "Multi-specialist coordination"
44
+ },
45
+ "COMPLEX": {
46
+ color: pc.red,
47
+ icon: `${S.RADIO_ACTIVE}${S.RADIO_ACTIVE}${S.RADIO_ACTIVE}`,
48
+ label: "Full Stack",
49
+ description: "Complete team mobilized"
50
+ }
51
+ };
52
+
53
+ // FAANG-level professional messages
54
+ const ROUTING_MESSAGES = {
55
+ simple: [
56
+ "Specialist locked in",
57
+ "Expert matched to your task",
58
+ "Best-fit agent selected",
59
+ "Precision routing complete"
60
+ ],
61
+ moderate: [
62
+ "Cross-functional team assembled",
63
+ "Specialists synchronized",
64
+ "Multi-domain expertise activated",
65
+ "Collaborative intelligence engaged"
66
+ ],
67
+ complex: [
68
+ "Full engineering squad deployed",
69
+ "Maximum capability unlocked",
70
+ "Enterprise-grade orchestration",
71
+ "Complete specialist coverage"
72
+ ]
73
+ };
74
+
75
+ // ============================================================================
76
+ // CORE FUNCTIONS
77
+ // ============================================================================
78
+
79
+ /**
80
+ * Get icon for an agent
81
+ * @param {string} agentName
82
+ * @param {boolean} isMain - Is this the main/first agent?
83
+ * @returns {string}
84
+ */
85
+ function getAgentIcon(agentName, isMain = false) {
86
+ if (isMain || agentName === "orchestrator") {
87
+ return S.STEP_ACTIVE;
88
+ }
89
+ return S.STEP_SUBMIT;
90
+ }
91
+
92
+ /**
93
+ * Format agent names with professional styling
94
+ * @param {string[]} agents - List of agent names
95
+ * @returns {string}
96
+ */
97
+ function formatAgents(agents) {
98
+ if (!agents || agents.length === 0) return pc.dim("analyzing...");
99
+
100
+ return agents
101
+ .map((agent, index) => {
102
+ const icon = getAgentIcon(agent, index === 0);
103
+ const name = `@${agent}`;
104
+ const colorFn = index === 0 ? pc.cyan : pc.blue;
105
+ return `${colorFn(icon)} ${pc.bold(colorFn(name))}`;
106
+ })
107
+ .join(pc.dim(" → "));
108
+ }
109
+
110
+ /**
111
+ * Format domains with professional styling
112
+ * @param {string[]} domains
113
+ * @returns {string}
114
+ */
115
+ function formatDomains(domains) {
116
+ if (!domains || domains.length === 0) return pc.dim("general");
117
+ return domains.map(d => pc.magenta(d)).join(pc.dim(" · "));
118
+ }
119
+
120
+ /**
121
+ * Get random message from array
122
+ * @param {string[]} messages
123
+ * @returns {string}
124
+ */
125
+ function getRandomMessage(messages) {
126
+ return messages[Math.floor(Math.random() * messages.length)];
127
+ }
128
+
129
+ /**
130
+ * Show routing result using Clack native styling
131
+ * @param {object} routing - Routing result object
132
+ */
133
+ export function showRoutingResult(routing) {
134
+ const { selectedAgents = [], domains = [], complexity = "SIMPLE", reasoning } = routing;
135
+
136
+ const config = COMPLEXITY_CONFIG[complexity] || COMPLEXITY_CONFIG.SIMPLE;
137
+
138
+ // Build content lines
139
+ const lines = [];
140
+ lines.push(`${pc.dim("Agents")} ${formatAgents(selectedAgents)}`);
141
+ lines.push(`${pc.dim("Domains")} ${formatDomains(domains)}`);
142
+ lines.push(`${pc.dim("Mode")} ${config.color(config.icon)} ${config.color(config.label)}`);
143
+
144
+ if (reasoning) {
145
+ lines.push("");
146
+ lines.push(`${pc.dim("→")} ${pc.italic(reasoning)}`);
147
+ }
148
+
149
+ // Use Clack's native note for consistent styling
150
+ p.note(lines.join("\n"), `${AGENT_EMOJI} Agent Routing`);
151
+ }
152
+
153
+ /**
154
+ * Show compact inline routing info
155
+ * @param {string[]} selectedAgents
156
+ */
157
+ export function showRoutingInline(selectedAgents) {
158
+ const formatted = selectedAgents
159
+ .map((agent, i) => `${getAgentIcon(agent, i === 0)} ${pc.cyan(`@${agent}`)}`)
160
+ .join(pc.dim(" → "));
161
+
162
+ console.log("");
163
+ console.log(`${pc.gray(S.BAR)}`);
164
+ console.log(`${pc.cyan(S.STEP_ACTIVE)} ${AGENT_EMOJI} ${pc.bold("Engaging")} ${formatted}`);
165
+ }
166
+
167
+ /**
168
+ * Show routing with Clack log message style
169
+ * @param {object} routing
170
+ */
171
+ export function showRoutingLog(routing) {
172
+ const { selectedAgents = [], complexity = "SIMPLE" } = routing;
173
+ const config = COMPLEXITY_CONFIG[complexity] || COMPLEXITY_CONFIG.SIMPLE;
174
+
175
+ const agentList = selectedAgents
176
+ .map((a, i) => `${getAgentIcon(a, i === 0)} @${a}`)
177
+ .join(" → ");
178
+
179
+ p.log.info(`${AGENT_EMOJI} ${pc.bold("Routing:")} ${pc.cyan(agentList)} ${config.color(`[${config.label}]`)}`);
180
+ }
181
+
182
+ /**
183
+ * Show routing step (for multi-step flows)
184
+ * @param {string} agent - Agent name
185
+ * @param {string} status - active | complete | pending
186
+ */
187
+ export function showRoutingStep(agent, status = "active") {
188
+ const icons = {
189
+ active: pc.cyan(S.STEP_ACTIVE),
190
+ complete: pc.green(S.STEP_SUBMIT),
191
+ pending: pc.dim(S.RADIO_INACTIVE)
192
+ };
193
+
194
+ const labels = {
195
+ active: pc.cyan("working"),
196
+ complete: pc.green("done"),
197
+ pending: pc.dim("queued")
198
+ };
199
+
200
+ const icon = icons[status] || icons.pending;
201
+ const label = labels[status] || labels.pending;
202
+ const name = status === "active" ? pc.bold(pc.cyan(`@${agent}`)) : pc.dim(`@${agent}`);
203
+
204
+ console.log(`${pc.gray(S.BAR)} ${icon} ${name} ${pc.dim("·")} ${label}`);
205
+ }
206
+
207
+ /**
208
+ * Analyze request and show routing decision
209
+ * @param {string} userRequest - The user's request text
210
+ * @returns {object} - The routing decision
211
+ */
212
+ export function analyzeAndShowRouting(userRequest) {
213
+ // Domain detection patterns
214
+ const patterns = {
215
+ "security": /auth|login|password|jwt|token|vulnerability|security|encrypt/i,
216
+ "frontend": /component|react|vue|css|html|tailwind|button|layout|style|ui/i,
217
+ "backend": /api|endpoint|server|express|route|post|get|database/i,
218
+ "mobile": /react native|flutter|ios|android|mobile|expo|screen/i,
219
+ "database": /prisma|sql|mongodb|schema|migration|table|query/i,
220
+ "testing": /test|jest|vitest|playwright|coverage|e2e/i,
221
+ "debug": /error|bug|crash|not working|broken|fix/i,
222
+ "performance": /slow|optimize|performance|speed|cache/i,
223
+ "analysis": /analyze|review|refactor|clean|improve|upgrade/i
224
+ };
225
+
226
+ // Detect domains
227
+ const domains = [];
228
+ for (const [domain, pattern] of Object.entries(patterns)) {
229
+ if (pattern.test(userRequest)) {
230
+ domains.push(domain);
231
+ }
232
+ }
233
+
234
+ // Determine complexity
235
+ let complexity = "SIMPLE";
236
+ let messagePool = ROUTING_MESSAGES.simple;
237
+
238
+ if (domains.length >= 3) {
239
+ complexity = "COMPLEX";
240
+ messagePool = ROUTING_MESSAGES.complex;
241
+ } else if (domains.length === 2) {
242
+ complexity = "MODERATE";
243
+ messagePool = ROUTING_MESSAGES.moderate;
244
+ }
245
+
246
+ // Select agents based on domains (FAANG-style short names)
247
+ const agentMap = {
248
+ "security": "security",
249
+ "frontend": "frontend",
250
+ "backend": "backend",
251
+ "mobile": "mobile",
252
+ "database": "database",
253
+ "testing": "testing",
254
+ "debug": "debug",
255
+ "performance": "perf",
256
+ "analysis": "analyst"
257
+ };
258
+
259
+ let selectedAgents = domains.map(d => agentMap[d] || "lead");
260
+
261
+ // If complex or no agents, add lead
262
+ if (complexity === "COMPLEX" || selectedAgents.length === 0) {
263
+ selectedAgents = ["lead", ...selectedAgents];
264
+ }
265
+
266
+ // Remove duplicates
267
+ selectedAgents = [...new Set(selectedAgents)];
268
+
269
+ const routing = {
270
+ selectedAgents,
271
+ domains,
272
+ complexity,
273
+ reasoning: getRandomMessage(messagePool)
274
+ };
275
+
276
+ // Show the routing result
277
+ showRoutingResult(routing);
278
+
279
+ return routing;
280
+ }
281
+
282
+ /**
283
+ * Show agent intro message
284
+ * @param {string} message - Custom message
285
+ */
286
+ export function showAgentIntro(message = "Ready to assist") {
287
+ p.intro(`${AGENT_EMOJI} ${pc.bold(message)}`);
288
+ }
289
+
290
+ /**
291
+ * Show agent outro message
292
+ * @param {string} message - Custom message
293
+ */
294
+ export function showAgentOutro(message = "Task complete") {
295
+ p.outro(`${AGENT_EMOJI} ${pc.green(message)}`);
296
+ }
297
+
298
+ /**
299
+ * Show agent thinking indicator
300
+ * @param {string} action - What the agent is doing
301
+ */
302
+ export function showAgentThinking(action = "Analyzing") {
303
+ console.log(`${pc.gray(S.BAR)}`);
304
+ console.log(`${pc.cyan(S.STEP_ACTIVE)} ${AGENT_EMOJI} ${pc.dim(action)}...`);
305
+ }
306
+
307
+ // ============================================================================
308
+ // EXPORTS
309
+ // ============================================================================
310
+
311
+ export default {
312
+ showRoutingResult,
313
+ showRoutingInline,
314
+ showRoutingLog,
315
+ showRoutingStep,
316
+ analyzeAndShowRouting,
317
+ showAgentIntro,
318
+ showAgentOutro,
319
+ showAgentThinking,
320
+ getAgentIcon,
321
+ AGENT_EMOJI,
322
+ S,
323
+ COMPLEXITY_CONFIG,
324
+ ROUTING_MESSAGES
325
+ };
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Settings UI - Interactive settings configuration
3
+ */
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+ import {
7
+ loadSettings,
8
+ saveSettings,
9
+ toggleAutoLearning,
10
+ toggleAutoUpdating,
11
+ setApiKey,
12
+ getApiKey,
13
+ removeApiKey
14
+ } from "../settings.js";
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import os from 'os';
18
+
19
+ // ============================================================================
20
+ // HELPERS
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Mask API key for display (show first 4 and last 4 chars)
25
+ */
26
+ function maskApiKey(key) {
27
+ if (!key) return pc.dim("[Not Set]");
28
+ if (key.length < 12) return "***...***";
29
+ return `${key.substring(0, 4)}...${key.substring(key.length - 4)}`;
30
+ }
31
+
32
+ /**
33
+ * Get Antigravity OAuth credentials (if available)
34
+ * @returns {{accessToken: string, expiryDate: number} | null}
35
+ */
36
+ function getAntigravityOAuthToken() {
37
+ try {
38
+ const credPath = path.join(os.homedir(), '.gemini', 'oauth_creds.json');
39
+
40
+ if (!fs.existsSync(credPath)) return null;
41
+
42
+ const creds = JSON.parse(fs.readFileSync(credPath, 'utf8'));
43
+
44
+ // Check if token is still valid
45
+ if (creds.access_token && creds.expiry_date) {
46
+ const now = Date.now();
47
+ if (now < creds.expiry_date) {
48
+ return {
49
+ accessToken: creds.access_token,
50
+ expiryDate: creds.expiry_date
51
+ };
52
+ }
53
+ }
54
+
55
+ return null;
56
+ } catch (e) {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Detect which API key Agent is actively using (from environment)
63
+ * Checks multiple sources in priority order:
64
+ * 1. Current process environment
65
+ * 2. Exported Settings API key
66
+ * 3. SelfEvolution .env file
67
+ * @returns {'gemini' | 'claude' | null}
68
+ */
69
+ function getActiveApiKey() {
70
+ // Priority 1: Current process environment (Agent session)
71
+ if (process.env.GEMINI_API_KEY) return 'gemini';
72
+ if (process.env.CLAUDE_API_KEY || process.env.ANTHROPIC_API_KEY) return 'claude';
73
+
74
+ // Priority 2: Check if we have exported keys from Settings
75
+ const geminiKey = getApiKey('gemini');
76
+ const claudeKey = getApiKey('claude');
77
+
78
+ // If Settings has a key configured, consider it "active"
79
+ // (because we auto-export to SelfEvolution)
80
+ if (geminiKey) return 'gemini';
81
+ if (claudeKey) return 'claude';
82
+
83
+ // Priority 3: Check Antigravity OAuth credentials
84
+ const oauthCreds = getAntigravityOAuthToken();
85
+ if (oauthCreds) return 'gemini'; // Antigravity uses Google OAuth
86
+
87
+ return null;
88
+ }
89
+
90
+ /**
91
+ * Format API key status (ON if active, OFF if not)
92
+ */
93
+ function formatApiKeyStatus(provider, storedKey) {
94
+ const activeKey = getActiveApiKey();
95
+ const isActive = activeKey === provider;
96
+
97
+ if (!storedKey) {
98
+ return pc.dim("[Not Set]");
99
+ }
100
+
101
+ if (isActive) {
102
+ return `${pc.green("[ON]")} ${maskApiKey(storedKey)}`;
103
+ } else {
104
+ return `${pc.dim("[OFF]")} ${maskApiKey(storedKey)}`;
105
+ }
106
+ }
107
+
108
+ // ============================================================================
109
+ // SETTINGS MENU
110
+ // ============================================================================
111
+
112
+ /**
113
+ * Interactive settings menu with Clack icons
114
+ */
115
+ export async function runSettingsUI() {
116
+ // No intro - go straight to settings menu
117
+
118
+ // Display active API key info ONCE at the start
119
+ const geminiKey = getApiKey('gemini');
120
+ const claudeKey = getApiKey('claude');
121
+
122
+ let bannerMsg;
123
+
124
+ // Priority 1: Check if running in Antigravity (check for oauth_creds.json)
125
+ try {
126
+ const credPath = path.join(os.homedir(), '.gemini', 'oauth_creds.json');
127
+ if (fs.existsSync(credPath)) {
128
+ // Running in Antigravity - always show this message
129
+ bannerMsg = pc.green(
130
+ `✓ Agent Coding is using system credentials\n` +
131
+ ` Source: Antigravity Session (in-memory)\n` +
132
+ ` → Configure permanent API key below for SelfEvolution features`
133
+ );
134
+ } else {
135
+ // Not in Antigravity, check for configured keys
136
+ if (geminiKey) {
137
+ bannerMsg = pc.green(`✓ Using Gemini API Key (Configured)\n → SelfEvolution features enabled`);
138
+ } else if (claudeKey) {
139
+ bannerMsg = pc.green(`✓ Using Claude API Key (Configured)\n → SelfEvolution features enabled`);
140
+ } else {
141
+ bannerMsg = pc.yellow(`⚠ No API Key Configured\n → Set up API key below to enable SelfEvolution features`);
142
+ }
143
+ }
144
+ } catch (e) {
145
+ // Fallback: check configured keys
146
+ if (geminiKey) {
147
+ bannerMsg = pc.green(`✓ Using Gemini API Key (Configured)\n → SelfEvolution features enabled`);
148
+ } else if (claudeKey) {
149
+ bannerMsg = pc.green(`✓ Using Claude API Key (Configured)\n → SelfEvolution features enabled`);
150
+ } else {
151
+ bannerMsg = pc.yellow(`⚠ No API Key Configured\n → Set up API key below to enable SelfEvolution features`);
152
+ }
153
+ }
154
+
155
+ p.note(bannerMsg, "🔑 Active API Key");
156
+
157
+ while (true) {
158
+ const settings = loadSettings();
159
+
160
+ const action = await p.select({
161
+ message: "⚙️ Settings",
162
+ options: [
163
+ // AI BEHAVIOR GROUP
164
+ {
165
+ value: "autoLearn",
166
+ label: `🤖 Auto-Learning: ${settings.autoLearning ? pc.green("[ON]") : pc.dim("[OFF]")}`,
167
+ hint: "Learn from mistakes"
168
+ },
169
+ {
170
+ value: "autoUpdate",
171
+ label: `🔄 Auto-Updating: ${settings.autoUpdating ? pc.green("[ON]") : pc.dim("[OFF]")}`,
172
+ hint: "Update threshold hits"
173
+ },
174
+ {
175
+ value: "threshold",
176
+ label: `📈 Update Threshold: ${pc.cyan(settings.updateThreshold)}`,
177
+ hint: "Hits before update"
178
+ },
179
+
180
+ // API CONFIGURATION GROUP
181
+ {
182
+ value: "geminiKey",
183
+ label: `🔑 Gemini API: ${formatApiKeyStatus('gemini', getApiKey('gemini'))}`,
184
+ hint: "For agent coding"
185
+ },
186
+ {
187
+ value: "claudeKey",
188
+ label: `🔑 Claude API: ${formatApiKeyStatus('claude', getApiKey('claude'))}`,
189
+ hint: "Alternative AI provider"
190
+ },
191
+ {
192
+ value: "testOptimization",
193
+ label: "🧪 Test AI Optimization",
194
+ hint: "Verify SelfEvolution integration"
195
+ },
196
+ { value: "back", label: "← Back", hint: "Return to main menu" }
197
+ ]
198
+ });
199
+
200
+ if (p.isCancel(action) || action === "back") {
201
+ return;
202
+ }
203
+
204
+ switch (action) {
205
+ case "autoLearn": {
206
+ const newValue = toggleAutoLearning();
207
+ p.note(
208
+ `Auto-Learning is now ${newValue ? pc.green("ON") : pc.red("OFF")}`,
209
+ "Setting Updated"
210
+ );
211
+ break; // Continue loop to show menu again
212
+ }
213
+ case "autoUpdate": {
214
+ const newValue = toggleAutoUpdating();
215
+ if (newValue) {
216
+ p.note(
217
+ `Auto-Updating is now ${pc.green("ON")}\n\n` +
218
+ `When patterns become valuable, Agent will:\n` +
219
+ `• Analyze learned lessons\n` +
220
+ `• Generate update proposals\n` +
221
+ `• Notify you for approval`,
222
+ "Setting Updated"
223
+ );
224
+ } else {
225
+ p.note(
226
+ `Auto-Updating is now ${pc.red("OFF")}`,
227
+ "Setting Updated"
228
+ );
229
+ }
230
+ break; // Continue loop
231
+ }
232
+ case "threshold": {
233
+ const newThreshold = await p.text({
234
+ message: "Set new threshold (1-20):",
235
+ placeholder: "5",
236
+ initialValue: String(settings.updateThreshold),
237
+ validate: (value) => {
238
+ const num = parseInt(value);
239
+ if (isNaN(num) || num < 1 || num > 20) {
240
+ return "Please enter a number between 1 and 20";
241
+ }
242
+ }
243
+ });
244
+
245
+ if (!p.isCancel(newThreshold)) {
246
+ settings.updateThreshold = parseInt(newThreshold);
247
+ saveSettings(settings);
248
+ p.note(
249
+ `Update threshold set to ${settings.updateThreshold}`,
250
+ "Setting Updated"
251
+ );
252
+ }
253
+ break; // Continue loop
254
+ }
255
+ case "geminiKey":
256
+ case "claudeKey": {
257
+ const provider = action === "geminiKey" ? "gemini" : "claude";
258
+ const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
259
+ const currentKey = getApiKey(provider);
260
+
261
+ // Ask user action
262
+ const keyAction = await p.select({
263
+ message: `${providerName} API Key:`,
264
+ options: [
265
+ { value: "set", label: "Set/Update Key", hint: "Configure API key" },
266
+ ...(currentKey ? [{ value: "remove", label: "Remove Key", hint: "Clear stored key" }] : []),
267
+ { value: "cancel", label: "Cancel", hint: "Go back" }
268
+ ]
269
+ });
270
+
271
+ if (p.isCancel(keyAction) || keyAction === "cancel") {
272
+ break; // Return to settings menu
273
+ }
274
+
275
+ if (keyAction === "set") {
276
+ const apiKey = await p.password({
277
+ message: `Enter ${providerName} API Key:`,
278
+ mask: "*",
279
+ validate: (value) => {
280
+ if (!value || value.length < 10) {
281
+ return "API key must be at least 10 characters";
282
+ }
283
+ }
284
+ });
285
+
286
+ if (!p.isCancel(apiKey)) {
287
+ const success = setApiKey(provider, apiKey);
288
+ if (success) {
289
+ p.note(
290
+ `${providerName} API key ${currentKey ? 'updated' : 'set'} successfully!\n\n` +
291
+ `Key: ${maskApiKey(apiKey)}\n` +
292
+ `Stored in: .agent/knowledge/settings.yaml`,
293
+ pc.green("✓ API Key Saved")
294
+ );
295
+ } else {
296
+ p.note(
297
+ `Failed to save ${providerName} API key`,
298
+ pc.red("✗ Error")
299
+ );
300
+ }
301
+ }
302
+ } else if (keyAction === "remove") {
303
+ const confirm = await p.confirm({
304
+ message: `Remove ${providerName} API key?`,
305
+ });
306
+
307
+ if (confirm && !p.isCancel(confirm)) {
308
+ removeApiKey(provider);
309
+ p.note(
310
+ `${providerName} API key removed`,
311
+ "Key Removed"
312
+ );
313
+ }
314
+ }
315
+ break;
316
+ }
317
+ case "testOptimization": {
318
+ const { exportApiKeysToSelfEvolution, verifySelfEvolutionAccess } =
319
+ await import('../selfevolution-bridge.js');
320
+
321
+ // Step 1: Export keys
322
+ const spinner = p.spinner();
323
+ spinner.start('Exporting API keys to SelfEvolution...');
324
+
325
+ const exportResult = exportApiKeysToSelfEvolution();
326
+
327
+ if (!exportResult.success) {
328
+ spinner.stop('Export failed');
329
+ p.note(
330
+ `❌ No API keys configured\n\n` +
331
+ `Please set Gemini or Claude API key first.\n\n` +
332
+ `Reason: ${exportResult.reason}`,
333
+ pc.red('✗ Cannot Test')
334
+ );
335
+ break;
336
+ }
337
+
338
+ spinner.message('Keys exported. Verifying Python access...');
339
+
340
+ // Step 2: Verify Python can access
341
+ const verifyResult = await verifySelfEvolutionAccess();
342
+
343
+ if (verifyResult.success && verifyResult.keyDetected) {
344
+ spinner.stop('Verification complete');
345
+
346
+ const exportedKeys = [];
347
+ if (exportResult.exported.gemini) exportedKeys.push('Gemini');
348
+ if (exportResult.exported.claude) exportedKeys.push('Claude');
349
+
350
+ p.note(
351
+ `✅ **API Key Integration Working!**\n\n` +
352
+ `Exported: ${exportedKeys.join(', ')}\n` +
353
+ `Location: ${exportResult.path}\n\n` +
354
+ `SelfEvolution can now:\n` +
355
+ `• Auto-learn from mistakes\n` +
356
+ `• AI-powered lesson optimization\n` +
357
+ `• Self-improve knowledge base\n\n` +
358
+ `${pc.dim('Python Output:')}\n${pc.dim(verifyResult.output)}`,
359
+ pc.green('✓ Optimization Ready')
360
+ );
361
+ } else {
362
+ spinner.stop('Verification failed');
363
+ p.note(
364
+ `⚠️ **Export OK, but verification failed**\n\n` +
365
+ `Keys exported to: ${exportResult.path}\n` +
366
+ `But Python script couldn't detect them.\n\n` +
367
+ `Error: ${verifyResult.error || 'Unknown'}\n\n` +
368
+ `Possible causes:\n` +
369
+ `• Python not installed\n` +
370
+ `• SelfEvolution scripts missing\n` +
371
+ `• Environment variable issue`,
372
+ pc.yellow('⚠ Partial Success')
373
+ );
374
+ }
375
+ break;
376
+ }
377
+ }
378
+ }
379
+ }
380
+
381
+ export default runSettingsUI;