ai-control-center 1.15.2

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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +584 -0
  3. package/bin/aicc.js +772 -0
  4. package/lib/actions/approve.js +71 -0
  5. package/lib/actions/assign-project.js +132 -0
  6. package/lib/actions/browser-test.js +64 -0
  7. package/lib/actions/cleanup.js +174 -0
  8. package/lib/actions/debug.js +298 -0
  9. package/lib/actions/deploy.js +1229 -0
  10. package/lib/actions/fix-bug.js +134 -0
  11. package/lib/actions/new-feature.js +255 -0
  12. package/lib/actions/reject.js +307 -0
  13. package/lib/actions/review.js +706 -0
  14. package/lib/actions/status.js +47 -0
  15. package/lib/agents/browser-qa-agent.js +611 -0
  16. package/lib/agents/payment-agent.js +116 -0
  17. package/lib/agents/suggestion-agent.js +88 -0
  18. package/lib/cli.js +303 -0
  19. package/lib/config.js +243 -0
  20. package/lib/hub/hub-server.js +440 -0
  21. package/lib/hub/project-poller.js +75 -0
  22. package/lib/hub/skill-registry.js +89 -0
  23. package/lib/hub/state-aggregator.js +204 -0
  24. package/lib/index.js +471 -0
  25. package/lib/init/doctor.js +523 -0
  26. package/lib/init/presets.js +222 -0
  27. package/lib/init/skill-fetcher.js +77 -0
  28. package/lib/init/wizard.js +973 -0
  29. package/lib/integrations/codex-runner.js +128 -0
  30. package/lib/integrations/github-actions.js +248 -0
  31. package/lib/integrations/github-reporter.js +229 -0
  32. package/lib/integrations/screenshot-store.js +102 -0
  33. package/lib/openclaw/bridge.js +650 -0
  34. package/lib/openclaw/generate-skill.js +235 -0
  35. package/lib/openclaw/openclaw.json +64 -0
  36. package/lib/orchestrator/autonomous-loop.js +429 -0
  37. package/lib/orchestrator/thread-triggers.js +63 -0
  38. package/lib/roleplay/agent-messenger.js +75 -0
  39. package/lib/roleplay/discussion-threads.js +303 -0
  40. package/lib/roleplay/health-monitor.js +121 -0
  41. package/lib/roleplay/pm-agent.js +513 -0
  42. package/lib/roleplay/roleplay-config.js +25 -0
  43. package/lib/roleplay/room.js +164 -0
  44. package/lib/shared/action-runner.js +2330 -0
  45. package/lib/shared/event-bus.js +185 -0
  46. package/lib/slack/bot.js +378 -0
  47. package/lib/telegram/bot.js +416 -0
  48. package/lib/telegram/commands.js +1267 -0
  49. package/lib/telegram/keyboards.js +113 -0
  50. package/lib/telegram/notifications.js +247 -0
  51. package/lib/twitch/bot.js +354 -0
  52. package/lib/twitch/commands.js +302 -0
  53. package/lib/twitch/notifications.js +63 -0
  54. package/lib/utils/achievements.js +191 -0
  55. package/lib/utils/activity-log.js +182 -0
  56. package/lib/utils/agent-leaderboard.js +119 -0
  57. package/lib/utils/audit-logger.js +232 -0
  58. package/lib/utils/codebase-context.js +288 -0
  59. package/lib/utils/codebase-indexer.js +381 -0
  60. package/lib/utils/config-schema.js +230 -0
  61. package/lib/utils/context-compressor.js +172 -0
  62. package/lib/utils/correlation.js +63 -0
  63. package/lib/utils/cost-tracker.js +423 -0
  64. package/lib/utils/cron-scheduler.js +53 -0
  65. package/lib/utils/db-adapter.js +293 -0
  66. package/lib/utils/display.js +272 -0
  67. package/lib/utils/errors.js +116 -0
  68. package/lib/utils/format.js +134 -0
  69. package/lib/utils/intent-engine.js +464 -0
  70. package/lib/utils/mcp-client.js +238 -0
  71. package/lib/utils/model-ab-test.js +164 -0
  72. package/lib/utils/notify.js +122 -0
  73. package/lib/utils/persona-loader.js +80 -0
  74. package/lib/utils/pipeline-lock.js +73 -0
  75. package/lib/utils/pipeline.js +214 -0
  76. package/lib/utils/plugin-runner.js +234 -0
  77. package/lib/utils/rate-limiter.js +84 -0
  78. package/lib/utils/rbac.js +74 -0
  79. package/lib/utils/runner.js +1809 -0
  80. package/lib/utils/security.js +191 -0
  81. package/lib/utils/self-healer.js +144 -0
  82. package/lib/utils/skill-loader.js +255 -0
  83. package/lib/utils/spinner.js +132 -0
  84. package/lib/utils/stage-queue.js +50 -0
  85. package/lib/utils/state-machine.js +89 -0
  86. package/lib/utils/status-bar.js +327 -0
  87. package/lib/utils/token-estimator.js +101 -0
  88. package/lib/utils/ux-analyzer.js +101 -0
  89. package/lib/utils/webhook-emitter.js +83 -0
  90. package/lib/web/public/css/styles.css +417 -0
  91. package/lib/web/public/dark-mode.js +44 -0
  92. package/lib/web/public/hub/kanban.html +206 -0
  93. package/lib/web/public/index.html +45 -0
  94. package/lib/web/public/js/app.js +71 -0
  95. package/lib/web/public/js/ask.js +110 -0
  96. package/lib/web/public/js/dashboard.js +165 -0
  97. package/lib/web/public/js/deploy.js +72 -0
  98. package/lib/web/public/js/feature.js +79 -0
  99. package/lib/web/public/js/health.js +65 -0
  100. package/lib/web/public/js/logs.js +93 -0
  101. package/lib/web/public/js/review.js +123 -0
  102. package/lib/web/public/js/ws-client.js +82 -0
  103. package/lib/web/public/office/css/office.css +678 -0
  104. package/lib/web/public/office/index.html +148 -0
  105. package/lib/web/public/office/js/achievements-ui.js +117 -0
  106. package/lib/web/public/office/js/character.js +1056 -0
  107. package/lib/web/public/office/js/chat-bubbles.js +177 -0
  108. package/lib/web/public/office/js/cost-overlay.js +123 -0
  109. package/lib/web/public/office/js/day-night.js +68 -0
  110. package/lib/web/public/office/js/effects.js +632 -0
  111. package/lib/web/public/office/js/engine.js +146 -0
  112. package/lib/web/public/office/js/feature-ticket.js +216 -0
  113. package/lib/web/public/office/js/hub-client.js +60 -0
  114. package/lib/web/public/office/js/main.js +1757 -0
  115. package/lib/web/public/office/js/office-layout.js +1524 -0
  116. package/lib/web/public/office/js/pathfinding.js +144 -0
  117. package/lib/web/public/office/js/pixel-sprites.js +1454 -0
  118. package/lib/web/public/office/js/progress-bars.js +117 -0
  119. package/lib/web/public/office/js/replay.js +191 -0
  120. package/lib/web/public/office/js/sound-effects.js +91 -0
  121. package/lib/web/public/office/js/sprite-renderer.js +211 -0
  122. package/lib/web/public/office/js/stamina-system.js +89 -0
  123. package/lib/web/public/office/js/ui.js +107 -0
  124. package/lib/web/public/onboarding/index.html +243 -0
  125. package/lib/web/public/timeline/index.html +195 -0
  126. package/lib/web/routes/api.js +499 -0
  127. package/lib/web/routes/logs.js +20 -0
  128. package/lib/web/routes/metrics.js +99 -0
  129. package/lib/web/server.js +183 -0
  130. package/lib/web/ws/handler.js +65 -0
  131. package/package.json +67 -0
  132. package/templates/agent-architect.md +69 -0
  133. package/templates/agent-gemini-pm.md +49 -0
  134. package/templates/agent-gemini-reviewer.md +52 -0
  135. package/templates/copilot-instructions.md +36 -0
  136. package/templates/pipelines/mobile.json +27 -0
  137. package/templates/pipelines/nodejs-api.json +27 -0
  138. package/templates/pipelines/python.json +27 -0
  139. package/templates/pipelines/react.json +27 -0
  140. package/templates/pipelines/salesforce.json +27 -0
  141. package/templates/role-gemini.md +97 -0
  142. package/templates/skill-architect.md +114 -0
  143. package/templates/skill-browser-qa.md +50 -0
  144. package/templates/skill-bug-from-qa.md +58 -0
  145. package/templates/skill-chatbot.md +93 -0
  146. package/templates/skill-implement.md +78 -0
  147. package/templates/skill-openclaw.md +174 -0
  148. package/templates/skill-payment.md +110 -0
  149. package/templates/skill-pm-spec.md +77 -0
  150. package/templates/skill-requirement-capture.md +97 -0
  151. package/templates/skill-review.md +108 -0
  152. package/templates/skill-reviewer-qa.md +44 -0
  153. package/templates/skill-suggestion.md +45 -0
  154. package/templates/skill-template.md +142 -0
@@ -0,0 +1,298 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { readdirSync, readFileSync, existsSync } from 'fs';
4
+ import { resolve } from 'path';
5
+ import { capture, run } from '../utils/runner.js';
6
+ import { logActivity, getSessionLogFile } from '../utils/activity-log.js';
7
+ import { getRootDir, getStatus, getWorkflowDir } from '../utils/pipeline.js';
8
+
9
+ export async function debugAction() {
10
+ const { choice } = await inquirer.prompt([{
11
+ type: 'list',
12
+ name: 'choice',
13
+ message: 'Debug / Logs',
14
+ prefix: chalk.cyan('◆'),
15
+ choices: [
16
+ { name: chalk.dim('1 ') + '▸ Tail Debug Logs (live stream)', value: 'tail' },
17
+ { name: chalk.dim('2 ') + '◎ Run All Tests', value: 'tests' },
18
+ { name: chalk.dim('3 ') + '◇ Run Specific Test Class', value: 'testclass' },
19
+ { name: chalk.dim('4 ') + '⊙ Open Default Org in Browser', value: 'open' },
20
+ { name: chalk.dim('5 ') + '◈ View Current Session Log (AI audit)', value: 'log-current' },
21
+ { name: chalk.dim('6 ') + '◈ Browse Past Session Logs', value: 'log-browse' },
22
+ new inquirer.Separator(chalk.dim(' ' + '─'.repeat(38))),
23
+ { name: chalk.dim('7 ') + '⊕ System Health Check (tools + org)', value: 'health' },
24
+ new inquirer.Separator(chalk.dim(' ' + '─'.repeat(38))),
25
+ { name: chalk.dim(' Back'), value: 'back' },
26
+ ],
27
+ pageSize: 11,
28
+ }]);
29
+
30
+ if (choice === 'back') return;
31
+
32
+ if (choice === 'tail') {
33
+ logActivity('SYSTEM', 'Tailing debug logs... (Ctrl+C to stop)', 'info');
34
+ await run('sf apex tail log', [], { ignoreExitCode: true });
35
+ }
36
+
37
+ if (choice === 'tests') {
38
+ logActivity('SYSTEM', 'Running all tests...', 'info');
39
+ await run('sf apex run test --test-level RunLocalTests --code-coverage --result-format human', [], { ignoreExitCode: true });
40
+ }
41
+
42
+ if (choice === 'testclass') {
43
+ const { className } = await inquirer.prompt([{
44
+ type: 'input',
45
+ name: 'className',
46
+ message: 'Test class name:',
47
+ prefix: chalk.cyan('◆'),
48
+ validate: v => v.trim().length > 0 || 'Class name required',
49
+ }]);
50
+ logActivity('SYSTEM', `Running test: ${className.trim()}`, 'info');
51
+ await run(`sf apex run test --class-names ${className.trim()} --result-format human --code-coverage`, [], { ignoreExitCode: true });
52
+ }
53
+
54
+ if (choice === 'open') {
55
+ logActivity('SYSTEM', 'Opening org in browser...', 'info');
56
+ await run('sf org open', [], { ignoreExitCode: true });
57
+ }
58
+
59
+ if (choice === 'log-current') {
60
+ await viewCurrentSessionLog();
61
+ }
62
+
63
+ if (choice === 'log-browse') {
64
+ await browsePastSessionLogs();
65
+ }
66
+
67
+ if (choice === 'health') {
68
+ await runHealthCheck();
69
+ }
70
+ }
71
+
72
+ // ─── System Health Check ──────────────────────────────────────────────────────
73
+
74
+ async function runHealthCheck() {
75
+ const cols = process.stdout.columns || 100;
76
+ console.log('');
77
+ console.log(chalk.cyan.bold(' System Health Check'));
78
+ console.log(chalk.dim(' ' + '─'.repeat(cols - 4)));
79
+
80
+ const check = async (label, cmd, args = [], successFn = null) => {
81
+ try {
82
+ const result = await capture(cmd, args);
83
+ const out = (result.stdout + result.stderr).trim().split('\n')[0];
84
+ const ok = result.code === 0;
85
+ const icon = ok ? chalk.green('✓') : chalk.red('✗');
86
+ const detail = ok && successFn ? successFn(out) : (ok ? out : chalk.red(out || 'not found'));
87
+ console.log(` ${icon} ${chalk.bold(label.padEnd(18))} ${chalk.dim(detail)}`);
88
+ return ok;
89
+ } catch {
90
+ console.log(` ${chalk.red('✗')} ${chalk.bold(label.padEnd(18))} ${chalk.red('not found')}`);
91
+ return false;
92
+ }
93
+ };
94
+
95
+ console.log(chalk.dim('\n AI Tools'));
96
+ await check('gemini', 'which gemini');
97
+ await check('claude', 'which claude');
98
+ await check('copilot', 'which copilot');
99
+ await check('gh', 'which gh');
100
+
101
+ console.log(chalk.dim('\n Platform CLI'));
102
+ const sfOk = await check('sf', 'which sf');
103
+ if (sfOk) {
104
+ await check('sf org', 'sf org display', [], out => out.slice(0, 60));
105
+ }
106
+
107
+ console.log(chalk.dim('\n Project Workflow'));
108
+ const root = getRootDir();
109
+ const wfDir = getWorkflowDir();
110
+ const skillsDir = resolve(root, '.claude/skills');
111
+ const agentsDir = resolve(root, '.claude/agents');
112
+
113
+ const dirCheck = (label, dir) => {
114
+ const ok = existsSync(dir);
115
+ const icon = ok ? chalk.green('✓') : chalk.yellow('~');
116
+ console.log(` ${icon} ${chalk.bold(label.padEnd(18))} ${chalk.dim(ok ? dir.replace(root + '/', '') : 'missing')}`);
117
+ };
118
+
119
+ dirCheck('.ai-workflow/', wfDir);
120
+ dirCheck('.claude/skills/', skillsDir);
121
+ dirCheck('.claude/agents/', agentsDir);
122
+
123
+ // Pipeline status
124
+ try {
125
+ const status = getStatus();
126
+ const stage = status.stage || 'none';
127
+ const feat = status.current_feature || 'none';
128
+ console.log(` ${chalk.cyan('◆')} ${chalk.bold('pipeline stage'.padEnd(18))} ${chalk.dim(`${stage} · ${feat}`)}`);
129
+ } catch {
130
+ console.log(` ${chalk.yellow('~')} ${chalk.bold('pipeline stage'.padEnd(18))} ${chalk.dim('could not read status.json')}`);
131
+ }
132
+
133
+ console.log('');
134
+ console.log(chalk.dim(' ' + '─'.repeat(cols - 4)));
135
+ console.log('');
136
+ }
137
+
138
+ // ─── AI Session Log Viewer ─────────────────────────────────────────────────────
139
+
140
+ /**
141
+ * View the log for the current running session.
142
+ */
143
+ async function viewCurrentSessionLog() {
144
+ const logFile = getSessionLogFile();
145
+ if (!logFile || !existsSync(logFile)) {
146
+ console.log(chalk.yellow('\n No session log found for this run.\n'));
147
+ return;
148
+ }
149
+ console.log(chalk.dim(`\n Session log: ${logFile}\n`));
150
+ printLog(logFile, true);
151
+ }
152
+
153
+ /**
154
+ * Browse and view past session log files.
155
+ */
156
+ async function browsePastSessionLogs() {
157
+ const logsDir = resolve(getWorkflowDir(), 'logs');
158
+ if (!existsSync(logsDir)) {
159
+ console.log(chalk.yellow('\n No session logs found yet. Logs are created when you start the control center.\n'));
160
+ return;
161
+ }
162
+
163
+ let files;
164
+ try {
165
+ files = readdirSync(logsDir)
166
+ .filter(f => f.startsWith('session-') && f.endsWith('.log'))
167
+ .sort()
168
+ .reverse() // newest first
169
+ .slice(0, 20);
170
+ } catch {
171
+ console.log(chalk.red('\n Could not read logs directory.\n'));
172
+ return;
173
+ }
174
+
175
+ if (files.length === 0) {
176
+ console.log(chalk.yellow('\n No session logs found.\n'));
177
+ return;
178
+ }
179
+
180
+ const choices = files.map((f, i) => ({
181
+ name: chalk.dim(`${i + 1} `) + formatLogFileName(f, logsDir),
182
+ value: resolve(logsDir, f),
183
+ }));
184
+ choices.push(new inquirer.Separator(chalk.dim(' ' + '─'.repeat(38))));
185
+ choices.push({ name: chalk.dim(' Back'), value: null });
186
+
187
+ const { logPath } = await inquirer.prompt([{
188
+ type: 'list',
189
+ name: 'logPath',
190
+ message: 'Select a session log to view',
191
+ prefix: chalk.cyan('◆'),
192
+ choices,
193
+ pageSize: 15,
194
+ }]);
195
+
196
+ if (!logPath) return;
197
+
198
+ console.log(chalk.dim(`\n ${logPath}\n`));
199
+ printLog(logPath, false);
200
+
201
+ // After viewing, offer to filter by agent
202
+ await filterLogByAgent(logPath);
203
+ }
204
+
205
+ /**
206
+ * Print a log file with colour-coded agent labels.
207
+ * If recentOnly=true, show last 60 lines (current session tail).
208
+ */
209
+ function printLog(logPath, recentOnly) {
210
+ try {
211
+ const content = readFileSync(logPath, 'utf8');
212
+ let lines = content.split('\n').filter(l => l.trim());
213
+ if (recentOnly && lines.length > 80) lines = lines.slice(-80);
214
+
215
+ const cols = process.stdout.columns || 100;
216
+ console.log(chalk.dim('─'.repeat(cols)));
217
+ for (const line of lines) {
218
+ console.log(colorizeLine(line));
219
+ }
220
+ console.log(chalk.dim('─'.repeat(cols)));
221
+ console.log(chalk.dim(`\n ${lines.length} lines shown${recentOnly ? ' (last 80)' : ''}.\n`));
222
+ } catch {
223
+ console.log(chalk.red('\n Could not read log file.\n'));
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Offer to filter and re-print the log by a specific agent.
229
+ */
230
+ async function filterLogByAgent(logPath) {
231
+ const { filter } = await inquirer.prompt([{
232
+ type: 'list',
233
+ name: 'filter',
234
+ message: 'Filter by agent?',
235
+ prefix: chalk.cyan('◆'),
236
+ choices: [
237
+ { name: chalk.dim('0 ') + 'Show all agents', value: null },
238
+ { name: chalk.blue.bold('COPILOT') + ' — what Copilot did and said', value: 'COPILOT' },
239
+ { name: chalk.cyan.bold('GEMINI ') + ' — Gemini analysis and output', value: 'GEMINI' },
240
+ { name: chalk.magenta.bold('CLAUDE ') + ' — Claude prompts and responses', value: 'CLAUDE' },
241
+ { name: chalk.yellow.bold('PIPELINE') + ' — deploy & pipeline events', value: 'PIPELINE' },
242
+ ],
243
+ }]);
244
+
245
+ if (!filter) return;
246
+
247
+ try {
248
+ const content = readFileSync(logPath, 'utf8');
249
+ const lines = content.split('\n').filter(l => l.trim() && l.includes(`[${filter}`));
250
+ if (lines.length === 0) {
251
+ console.log(chalk.dim(`\n No entries for ${filter}.\n`));
252
+ return;
253
+ }
254
+ const cols = process.stdout.columns || 100;
255
+ console.log('');
256
+ console.log(chalk.dim('─'.repeat(cols)));
257
+ for (const line of lines) console.log(colorizeLine(line));
258
+ console.log(chalk.dim('─'.repeat(cols)));
259
+ console.log(chalk.dim(`\n ${lines.length} entries for ${filter}.\n`));
260
+ } catch { /* skip */ }
261
+ }
262
+
263
+ /**
264
+ * Colourize a plain log line based on the agent label.
265
+ */
266
+ function colorizeLine(line) {
267
+ if (line.startsWith('=') || line.startsWith('Started') || line.startsWith('Log file')) {
268
+ return chalk.dim(line);
269
+ }
270
+
271
+ if (line.includes('[COPILOT')) return chalk.blue(line);
272
+ if (line.includes('[GEMINI')) return chalk.cyan(line);
273
+ if (line.includes('[CLAUDE')) return chalk.magenta(line);
274
+ if (line.includes('[PIPELINE')) return chalk.yellow(line);
275
+ if (line.includes('[ERROR')) return chalk.red(line);
276
+ if (line.includes('[WARN')) return chalk.yellow(line);
277
+ if (line.includes('[SUCCESS')) return chalk.green(line);
278
+ if (line.includes('[RAW')) return chalk.dim(line);
279
+ return chalk.white(line);
280
+ }
281
+
282
+ /**
283
+ * Format a log filename for display — show timestamp and rough line count.
284
+ */
285
+ function formatLogFileName(filename, logsDir) {
286
+ // session-2026-02-24T22-38-25.log → 2026-02-24 22:38:25
287
+ const match = filename.match(/session-(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})\.log/);
288
+ const label = match
289
+ ? `${match[1]} ${match[2]}:${match[3]}:${match[4]}`
290
+ : filename;
291
+
292
+ try {
293
+ const size = readFileSync(resolve(logsDir, filename), 'utf8').split('\n').filter(l => l.trim()).length;
294
+ return `${label} ${chalk.dim(`(${size} lines)`)}`;
295
+ } catch {
296
+ return label;
297
+ }
298
+ }