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,107 @@
1
+ export class UI {
2
+ constructor(engine, hub) {
3
+ this.engine = engine;
4
+ this.hub = hub;
5
+ this.feedList = document.getElementById('feed-list');
6
+
7
+ // Click on canvas
8
+ engine.canvas.addEventListener('click', (e) => this._onCanvasClick(e));
9
+
10
+ // Listen for pipeline events
11
+ hub.on('message', (data) => this._onEvent(data));
12
+
13
+ // Listen for state updates
14
+ hub.on('pipeline-event', (data) => {
15
+ if (data.stage) engine.updateState({ stage: data.stage });
16
+ if (data.message) this.addFeedItem(data.message);
17
+ if (data.type === 'stage_complete') {
18
+ const roleMap = { spec_complete: 'pm', arch_complete: 'architect', implementation_complete: 'coder', deployed: 'deployer' };
19
+ const role = roleMap[data.stage];
20
+ if (role) engine.spawnConfetti(role);
21
+ }
22
+ });
23
+ }
24
+
25
+ _onCanvasClick(e) {
26
+ const rect = this.engine.canvas.getBoundingClientRect();
27
+ const mx = e.clientX - rect.left, my = e.clientY - rect.top;
28
+ const ch = this.engine.getCharacterAt(mx, my);
29
+ if (ch) this.showCharacterPopup(ch);
30
+ }
31
+
32
+ _onEvent(data) {
33
+ if (data.type === 'status_update' && data.status) {
34
+ this.engine.updateState(data.status);
35
+ }
36
+ if (data.message) this.addFeedItem(data.message, data.type);
37
+ }
38
+
39
+ addFeedItem(message, type = '') {
40
+ const li = document.createElement('li');
41
+ const time = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit' });
42
+ const icon = type.includes('error') ? '❌' : type.includes('complete') ? '✅' : 'ℹ️';
43
+ li.innerHTML = `<span class="feed-time">[${time}]</span><span class="feed-icon">${icon}</span>${message}`;
44
+ this.feedList.prepend(li);
45
+ while (this.feedList.children.length > 50) this.feedList.lastChild.remove();
46
+ }
47
+
48
+ showCharacterPopup(ch) {
49
+ const overlay = document.getElementById('popup-overlay');
50
+ overlay.classList.remove('hidden');
51
+ overlay.innerHTML = `
52
+ <div class="popup-card">
53
+ <h2>${ch.emoji} ${ch.label}</h2>
54
+ <p><strong>Status:</strong> ${ch.state}</p>
55
+ <p><strong>Role:</strong> ${ch.role}</p>
56
+ <p><strong>Stage:</strong> ${this.engine.state.stage || 'idle'}</p>
57
+ <p><strong>Feature:</strong> ${this.engine.state.current_feature || 'None'}</p>
58
+ <div class="btn-row">
59
+ <button class="btn-cancel" onclick="document.getElementById('popup-overlay').classList.add('hidden')">Close</button>
60
+ </div>
61
+ </div>`;
62
+ }
63
+
64
+ showFeatureForm() {
65
+ const overlay = document.getElementById('popup-overlay');
66
+ overlay.classList.remove('hidden');
67
+ overlay.innerHTML = `
68
+ <div class="popup-card">
69
+ <h2>📋 Submit Feature</h2>
70
+ <label>Type</label>
71
+ <select id="ff-type"><option value="feature">Feature</option><option value="bug">Bug Fix</option></select>
72
+ <label>Mode</label>
73
+ <select id="ff-mode"><option value="auto">Auto (Full Pipeline)</option><option value="manual">Manual (Step by Step)</option></select>
74
+ <label>Description</label>
75
+ <textarea id="ff-desc" placeholder="Describe the feature or bug..."></textarea>
76
+ <div class="btn-row">
77
+ <button class="btn-cancel" onclick="document.getElementById('popup-overlay').classList.add('hidden')">Cancel</button>
78
+ <button class="btn-primary" id="ff-submit">🚀 Submit</button>
79
+ </div>
80
+ </div>`;
81
+ document.getElementById('ff-submit').addEventListener('click', () => this._submitFeature());
82
+ }
83
+
84
+ async _submitFeature() {
85
+ const desc = document.getElementById('ff-desc').value.trim();
86
+ const mode = document.getElementById('ff-mode').value;
87
+ const type = document.getElementById('ff-type').value;
88
+ if (!desc) return;
89
+ document.getElementById('popup-overlay').classList.add('hidden');
90
+ this.addFeedItem(`Submitting ${type}: "${desc.slice(0, 60)}..."`, 'feature_submit');
91
+ try {
92
+ const res = await fetch('/api/feature', {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify({ description: desc, mode, type }),
96
+ });
97
+ const result = await res.json();
98
+ if (result.success !== false) {
99
+ this.addFeedItem(`✅ Feature submitted: ${result.featureId || desc.slice(0, 40)}`, 'feature_created');
100
+ } else {
101
+ this.addFeedItem(`❌ Submit failed: ${result.error}`, 'error');
102
+ }
103
+ } catch (err) {
104
+ this.addFeedItem(`❌ Submit error: ${err.message}`, 'error');
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,243 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Control Center - Setup Wizard</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
10
+ .wizard { max-width: 640px; width: 100%; padding: 2rem; }
11
+ .wizard h1 { font-size: 2rem; margin-bottom: 0.5rem; }
12
+ .wizard .subtitle { color: #94a3b8; margin-bottom: 2rem; }
13
+ .progress { display: flex; gap: 4px; margin-bottom: 2rem; }
14
+ .progress .step { flex: 1; height: 4px; background: #334155; border-radius: 2px; transition: background 0.3s; }
15
+ .progress .step.active { background: #3b82f6; }
16
+ .progress .step.done { background: #22c55e; }
17
+ .step-content { background: #1e293b; border-radius: 12px; padding: 2rem; margin-bottom: 1.5rem; min-height: 300px; }
18
+ .step-content h2 { font-size: 1.25rem; margin-bottom: 1rem; }
19
+ .step-content p { color: #94a3b8; margin-bottom: 1rem; line-height: 1.6; }
20
+ .form-group { margin-bottom: 1rem; }
21
+ .form-group label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
22
+ .form-group input, .form-group select { width: 100%; padding: 0.75rem; background: #0f172a; border: 1px solid #334155; border-radius: 8px; color: #e2e8f0; font-size: 0.875rem; }
23
+ .form-group input:focus, .form-group select:focus { outline: none; border-color: #3b82f6; }
24
+ .option-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
25
+ .option-card { padding: 1rem; background: #0f172a; border: 2px solid #334155; border-radius: 8px; cursor: pointer; transition: all 0.2s; text-align: center; }
26
+ .option-card:hover { border-color: #3b82f6; }
27
+ .option-card.selected { border-color: #3b82f6; background: #1e3a5f; }
28
+ .option-card .emoji { font-size: 2rem; display: block; margin-bottom: 0.5rem; }
29
+ .option-card .name { font-weight: 600; }
30
+ .option-card .desc { font-size: 0.75rem; color: #94a3b8; margin-top: 0.25rem; }
31
+ .btn-row { display: flex; justify-content: space-between; }
32
+ .btn { padding: 0.75rem 1.5rem; border-radius: 8px; border: none; font-size: 0.875rem; font-weight: 600; cursor: pointer; transition: all 0.2s; }
33
+ .btn-primary { background: #3b82f6; color: white; }
34
+ .btn-primary:hover { background: #2563eb; }
35
+ .btn-secondary { background: #334155; color: #e2e8f0; }
36
+ .btn-secondary:hover { background: #475569; }
37
+ .btn:disabled { opacity: 0.5; cursor: not-allowed; }
38
+ .code-block { background: #0f172a; padding: 1rem; border-radius: 8px; font-family: 'SF Mono', monospace; font-size: 0.8rem; overflow-x: auto; margin-top: 1rem; border: 1px solid #334155; white-space: pre; }
39
+ .check-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; background: #0f172a; border-radius: 8px; margin-bottom: 0.5rem; }
40
+ .check-item .status { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; }
41
+ .check-item .status.pass { background: #166534; color: #4ade80; }
42
+ .check-item .status.fail { background: #7f1d1d; color: #fca5a5; }
43
+ .check-item .status.pending { background: #334155; color: #94a3b8; }
44
+ .confetti { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 100; }
45
+ </style>
46
+ </head>
47
+ <body>
48
+ <div class="wizard">
49
+ <h1>🤖 AI Control Center</h1>
50
+ <p class="subtitle">Let's get your AI-powered development pipeline set up</p>
51
+
52
+ <div class="progress" id="progress"></div>
53
+ <div class="step-content" id="stepContent"></div>
54
+ <div class="btn-row">
55
+ <button class="btn btn-secondary" id="prevBtn" onclick="prevStep()">← Back</button>
56
+ <button class="btn btn-primary" id="nextBtn" onclick="nextStep()">Next →</button>
57
+ </div>
58
+ </div>
59
+
60
+ <script>
61
+ let currentStep = 0;
62
+ const config = { models: ['gemini'], primaryModel: 'gemini', projectName: '', features: [] };
63
+
64
+ const steps = [
65
+ { title: '👋 Welcome', render: renderWelcome },
66
+ { title: '🤖 AI Models', render: renderModels },
67
+ { title: '📁 Project Setup', render: renderProject },
68
+ { title: '🔧 Features', render: renderFeatures },
69
+ { title: '✅ Pre-flight Check', render: renderPreflight },
70
+ { title: '🚀 Ready!', render: renderComplete },
71
+ ];
72
+
73
+ function renderProgress() {
74
+ const el = document.getElementById('progress');
75
+ el.innerHTML = steps.map((_, i) =>
76
+ `<div class="step ${i < currentStep ? 'done' : ''} ${i === currentStep ? 'active' : ''}"></div>`
77
+ ).join('');
78
+ }
79
+
80
+ function renderWelcome() {
81
+ return `
82
+ <h2>👋 Welcome to AI Control Center</h2>
83
+ <p>This wizard will help you configure your AI-powered development pipeline in just a few steps.</p>
84
+ <p>AICC orchestrates multiple AI models to handle your entire development workflow — from feature specs to code review and deployment.</p>
85
+ <div style="margin-top:1.5rem">
86
+ <div class="check-item"><div class="status pending">1</div><span>Choose your AI models</span></div>
87
+ <div class="check-item"><div class="status pending">2</div><span>Configure your project</span></div>
88
+ <div class="check-item"><div class="status pending">3</div><span>Select features</span></div>
89
+ <div class="check-item"><div class="status pending">4</div><span>Run pre-flight checks</span></div>
90
+ </div>
91
+ `;
92
+ }
93
+
94
+ function renderModels() {
95
+ const models = [
96
+ { id: 'gemini', emoji: '💎', name: 'Gemini', desc: 'Google AI (recommended)' },
97
+ { id: 'claude', emoji: '🧠', name: 'Claude', desc: 'Anthropic AI' },
98
+ { id: 'copilot', emoji: '🐙', name: 'Copilot', desc: 'GitHub Copilot CLI' },
99
+ { id: 'codex', emoji: '⚡', name: 'Codex', desc: 'OpenAI Codex CLI' },
100
+ ];
101
+ return `
102
+ <h2>🤖 Select AI Models</h2>
103
+ <p>Choose which AI models you want to use. You can select multiple models for A/B testing.</p>
104
+ <div class="option-grid">
105
+ ${models.map(m => `
106
+ <div class="option-card ${config.models.includes(m.id) ? 'selected' : ''}" onclick="toggleModel('${m.id}')">
107
+ <span class="emoji">${m.emoji}</span>
108
+ <div class="name">${m.name}</div>
109
+ <div class="desc">${m.desc}</div>
110
+ </div>
111
+ `).join('')}
112
+ </div>
113
+ `;
114
+ }
115
+
116
+ function renderProject() {
117
+ return `
118
+ <h2>📁 Project Configuration</h2>
119
+ <div class="form-group">
120
+ <label>Project Name</label>
121
+ <input type="text" id="projectName" value="${config.projectName}" placeholder="my-awesome-project" onchange="config.projectName=this.value">
122
+ </div>
123
+ <div class="form-group">
124
+ <label>Primary Model</label>
125
+ <select id="primaryModel" onchange="config.primaryModel=this.value">
126
+ ${config.models.map(m => `<option value="${m}" ${m === config.primaryModel ? 'selected' : ''}>${m}</option>`).join('')}
127
+ </select>
128
+ </div>
129
+ <div class="form-group">
130
+ <label>Pipeline Template</label>
131
+ <select id="template">
132
+ <option value="default">Default (Full Pipeline)</option>
133
+ <option value="react">React/Next.js</option>
134
+ <option value="nodejs-api">Node.js API</option>
135
+ <option value="python">Python</option>
136
+ <option value="mobile">Mobile App</option>
137
+ </select>
138
+ </div>
139
+ `;
140
+ }
141
+
142
+ function renderFeatures() {
143
+ const features = [
144
+ { id: 'webhooks', emoji: '🔗', name: 'Webhooks', desc: 'HTTP notifications' },
145
+ { id: 'slack', emoji: '💬', name: 'Slack Bot', desc: 'Slack integration' },
146
+ { id: 'hub', emoji: '🏢', name: 'Hub Mode', desc: 'Multi-project dashboard' },
147
+ { id: 'rbac', emoji: '🔒', name: 'RBAC', desc: 'Access control' },
148
+ { id: 'achievements', emoji: '🏆', name: 'Achievements', desc: 'Gamification' },
149
+ { id: 'office', emoji: '🎮', name: 'Office Visualizer', desc: 'Animated AI office' },
150
+ ];
151
+ return `
152
+ <h2>🔧 Optional Features</h2>
153
+ <p>Enable additional features (you can change these later in aicc.config.js)</p>
154
+ <div class="option-grid">
155
+ ${features.map(f => `
156
+ <div class="option-card ${config.features.includes(f.id) ? 'selected' : ''}" onclick="toggleFeature('${f.id}')">
157
+ <span class="emoji">${f.emoji}</span>
158
+ <div class="name">${f.name}</div>
159
+ <div class="desc">${f.desc}</div>
160
+ </div>
161
+ `).join('')}
162
+ </div>
163
+ `;
164
+ }
165
+
166
+ function renderPreflight() {
167
+ return `
168
+ <h2>✅ Pre-flight Checks</h2>
169
+ <p>Checking your environment...</p>
170
+ <div id="checks">
171
+ <div class="check-item"><div class="status pending" id="chk-node">⏳</div><span>Node.js ≥ 18</span></div>
172
+ <div class="check-item"><div class="status pending" id="chk-git">⏳</div><span>Git repository</span></div>
173
+ <div class="check-item"><div class="status pending" id="chk-model">⏳</div><span>AI model configured</span></div>
174
+ <div class="check-item"><div class="status pending" id="chk-dir">⏳</div><span>.ai-workflow directory</span></div>
175
+ </div>
176
+ <p style="margin-top:1rem;font-size:0.8rem;color:#64748b">These checks run locally in your browser as a visual guide. Actual validation happens when you run <code>aicc init</code>.</p>
177
+ `;
178
+ }
179
+
180
+ function renderComplete() {
181
+ const configStr = JSON.stringify({
182
+ models: config.models,
183
+ primaryModel: config.primaryModel,
184
+ features: config.features,
185
+ }, null, 2);
186
+ return `
187
+ <h2>🚀 You're All Set!</h2>
188
+ <p>Run these commands to initialize your project:</p>
189
+ <div class="code-block">npx aicc init
190
+ aicc health
191
+ aicc feature "My first AI feature"</div>
192
+ <p style="margin-top:1rem">Your configuration:</p>
193
+ <div class="code-block">${configStr}</div>
194
+ <p style="margin-top:1rem">📖 <a href="https://github.com/ai-control-center" style="color:#3b82f6">Documentation</a> | 💬 <a href="/office" style="color:#3b82f6">Open Office Visualizer</a></p>
195
+ `;
196
+ }
197
+
198
+ function toggleModel(id) {
199
+ const idx = config.models.indexOf(id);
200
+ if (idx >= 0) config.models.splice(idx, 1);
201
+ else config.models.push(id);
202
+ if (!config.models.includes(config.primaryModel)) config.primaryModel = config.models[0] || 'gemini';
203
+ renderStep();
204
+ }
205
+
206
+ function toggleFeature(id) {
207
+ const idx = config.features.indexOf(id);
208
+ if (idx >= 0) config.features.splice(idx, 1);
209
+ else config.features.push(id);
210
+ renderStep();
211
+ }
212
+
213
+ function renderStep() {
214
+ document.getElementById('stepContent').innerHTML = steps[currentStep].render();
215
+ renderProgress();
216
+ document.getElementById('prevBtn').disabled = currentStep === 0;
217
+ document.getElementById('nextBtn').textContent = currentStep === steps.length - 1 ? '✨ Done' : 'Next →';
218
+
219
+ if (currentStep === 4) runPreflight();
220
+ }
221
+
222
+ function prevStep() { if (currentStep > 0) { currentStep--; renderStep(); } }
223
+ function nextStep() {
224
+ if (currentStep < steps.length - 1) { currentStep++; renderStep(); }
225
+ else { window.location.href = '/'; }
226
+ }
227
+
228
+ function runPreflight() {
229
+ setTimeout(() => setCheck('chk-node', true), 500);
230
+ setTimeout(() => setCheck('chk-git', true), 1000);
231
+ setTimeout(() => setCheck('chk-model', config.models.length > 0), 1500);
232
+ setTimeout(() => setCheck('chk-dir', true), 2000);
233
+ }
234
+
235
+ function setCheck(id, pass) {
236
+ const el = document.getElementById(id);
237
+ if (el) { el.className = 'status ' + (pass ? 'pass' : 'fail'); el.textContent = pass ? '✓' : '✗'; }
238
+ }
239
+
240
+ renderStep();
241
+ </script>
242
+ </body>
243
+ </html>
@@ -0,0 +1,195 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AICC - Pipeline Timeline</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; }
10
+ .header { padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #1e293b; }
11
+ .header h1 { font-size: 1.25rem; }
12
+ .header .controls { display: flex; gap: 0.5rem; }
13
+ .header .controls button { padding: 0.5rem 1rem; background: #1e293b; border: 1px solid #334155; border-radius: 6px; color: #e2e8f0; cursor: pointer; font-size: 0.8rem; }
14
+ .header .controls button.active { background: #3b82f6; border-color: #3b82f6; }
15
+ .timeline-container { padding: 2rem; overflow-x: auto; }
16
+ .timeline { position: relative; min-height: 400px; }
17
+ .timeline-line { position: absolute; left: 24px; top: 0; bottom: 0; width: 2px; background: #334155; }
18
+ .timeline-item { position: relative; padding-left: 60px; margin-bottom: 1.5rem; }
19
+ .timeline-item .dot { position: absolute; left: 16px; top: 8px; width: 18px; height: 18px; border-radius: 50%; border: 3px solid; }
20
+ .timeline-item .dot.success { border-color: #22c55e; background: #166534; }
21
+ .timeline-item .dot.error { border-color: #ef4444; background: #7f1d1d; }
22
+ .timeline-item .dot.running { border-color: #3b82f6; background: #1e3a5f; animation: pulse 2s infinite; }
23
+ .timeline-item .dot.pending { border-color: #64748b; background: #334155; }
24
+ @keyframes pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(59,130,246,0.4); } 50% { box-shadow: 0 0 0 8px rgba(59,130,246,0); } }
25
+ .timeline-item .card { background: #1e293b; border-radius: 8px; padding: 1rem; border: 1px solid #334155; }
26
+ .timeline-item .card:hover { border-color: #475569; }
27
+ .timeline-item .card .title { font-weight: 600; margin-bottom: 0.25rem; }
28
+ .timeline-item .card .meta { font-size: 0.75rem; color: #64748b; display: flex; gap: 1rem; margin-bottom: 0.5rem; }
29
+ .timeline-item .card .stages { display: flex; gap: 4px; margin-top: 0.5rem; }
30
+ .timeline-item .card .stage { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: 500; }
31
+ .stage.spec { background: #1e3a5f; color: #60a5fa; }
32
+ .stage.arch { background: #14532d; color: #4ade80; }
33
+ .stage.impl { background: #713f12; color: #fbbf24; }
34
+ .stage.review { background: #4c1d95; color: #a78bfa; }
35
+ .stage.deploy { background: #7f1d1d; color: #fca5a5; }
36
+ .stats-bar { display: flex; gap: 2rem; padding: 1rem 2rem; background: #1e293b; border-bottom: 1px solid #334155; }
37
+ .stat { text-align: center; }
38
+ .stat .value { font-size: 1.5rem; font-weight: 700; }
39
+ .stat .label { font-size: 0.7rem; color: #64748b; text-transform: uppercase; }
40
+ .empty { text-align: center; padding: 4rem 2rem; color: #64748b; }
41
+ .empty .emoji { font-size: 3rem; margin-bottom: 1rem; display: block; }
42
+ .dark-toggle { position: fixed; bottom: 1rem; right: 1rem; padding: 0.5rem 1rem; background: #334155; border: none; border-radius: 8px; color: #e2e8f0; cursor: pointer; font-size: 0.8rem; }
43
+ body.light { background: #f8fafc; color: #1e293b; }
44
+ body.light .header { border-color: #e2e8f0; }
45
+ body.light .stats-bar { background: #fff; border-color: #e2e8f0; }
46
+ body.light .timeline-line { background: #e2e8f0; }
47
+ body.light .timeline-item .card { background: #fff; border-color: #e2e8f0; }
48
+ body.light .header .controls button { background: #fff; border-color: #e2e8f0; color: #1e293b; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="header">
53
+ <h1>📊 Pipeline Timeline</h1>
54
+ <div class="controls">
55
+ <button class="active" onclick="setFilter('all')">All</button>
56
+ <button onclick="setFilter('feature')">Features</button>
57
+ <button onclick="setFilter('review')">Reviews</button>
58
+ <button onclick="setFilter('deploy')">Deploys</button>
59
+ <a href="/" style="padding:0.5rem 1rem;background:#334155;border:1px solid #475569;border-radius:6px;color:#e2e8f0;text-decoration:none;font-size:0.8rem">← Dashboard</a>
60
+ </div>
61
+ </div>
62
+ <div class="stats-bar" id="statsBar"></div>
63
+ <div class="timeline-container">
64
+ <div class="timeline" id="timeline">
65
+ <div class="timeline-line"></div>
66
+ </div>
67
+ </div>
68
+ <button class="dark-toggle" onclick="toggleTheme()">🌓 Toggle Theme</button>
69
+
70
+ <script>
71
+ let pipelines = [];
72
+ let filter = 'all';
73
+ let isDark = true;
74
+
75
+ async function fetchPipelines() {
76
+ try {
77
+ const res = await fetch('/api/status');
78
+ const data = await res.json();
79
+ // Build timeline from available data
80
+ if (data.history) {
81
+ pipelines = data.history;
82
+ } else {
83
+ // Construct from current status
84
+ pipelines = [{
85
+ id: 1,
86
+ action: data.currentAction || 'idle',
87
+ status: data.phase === 'idle' ? 'success' : 'running',
88
+ startTime: data.startTime || Date.now(),
89
+ duration: data.duration || 0,
90
+ cost: data.totalCost || 0,
91
+ model: data.model || 'gemini',
92
+ stages: data.completedStages || [],
93
+ }];
94
+ }
95
+ } catch (e) {
96
+ pipelines = [];
97
+ }
98
+ render();
99
+ }
100
+
101
+ function render() {
102
+ const filtered = filter === 'all' ? pipelines : pipelines.filter(p => p.action === filter);
103
+ renderStats(filtered);
104
+ renderTimeline(filtered);
105
+ }
106
+
107
+ function renderStats(items) {
108
+ const total = items.length;
109
+ const successful = items.filter(p => p.status === 'success').length;
110
+ const totalCost = items.reduce((s, p) => s + (p.cost || 0), 0);
111
+ const avgDuration = total ? items.reduce((s, p) => s + (p.duration || 0), 0) / total : 0;
112
+ document.getElementById('statsBar').innerHTML = `
113
+ <div class="stat"><div class="value">${total}</div><div class="label">Total Runs</div></div>
114
+ <div class="stat"><div class="value">${successful}</div><div class="label">Successful</div></div>
115
+ <div class="stat"><div class="value">${total ? Math.round(successful/total*100) : 0}%</div><div class="label">Success Rate</div></div>
116
+ <div class="stat"><div class="value">$${totalCost.toFixed(2)}</div><div class="label">Total Cost</div></div>
117
+ <div class="stat"><div class="value">${formatDuration(avgDuration)}</div><div class="label">Avg Duration</div></div>
118
+ `;
119
+ }
120
+
121
+ function renderTimeline(items) {
122
+ const el = document.getElementById('timeline');
123
+ if (items.length === 0) {
124
+ el.innerHTML = '<div class="timeline-line"></div><div class="empty"><span class="emoji">📭</span>No pipeline runs yet.<br>Run <code>aicc feature "something"</code> to get started.</div>';
125
+ return;
126
+ }
127
+ el.innerHTML = '<div class="timeline-line"></div>' + items.map(p => `
128
+ <div class="timeline-item">
129
+ <div class="dot ${p.status}"></div>
130
+ <div class="card">
131
+ <div class="title">${getActionEmoji(p.action)} ${p.action || 'Pipeline'} ${p.description ? '— ' + p.description : ''}</div>
132
+ <div class="meta">
133
+ <span>🕐 ${formatTime(p.startTime)}</span>
134
+ <span>⏱️ ${formatDuration(p.duration)}</span>
135
+ <span>💰 $${(p.cost || 0).toFixed(4)}</span>
136
+ <span>🤖 ${p.model || 'unknown'}</span>
137
+ </div>
138
+ <div class="stages">
139
+ ${['spec', 'arch', 'impl', 'review', 'deploy'].map(s =>
140
+ `<span class="stage ${s}" style="opacity:${(p.stages || []).includes(s) ? 1 : 0.3}">${s}</span>`
141
+ ).join('')}
142
+ </div>
143
+ </div>
144
+ </div>
145
+ `).join('');
146
+ }
147
+
148
+ function getActionEmoji(action) {
149
+ return { feature: '🎯', review: '🔍', deploy: '🚀', fix: '🔧' }[action] || '⚡';
150
+ }
151
+
152
+ function formatTime(ts) {
153
+ if (!ts) return 'N/A';
154
+ return new Date(ts).toLocaleString();
155
+ }
156
+
157
+ function formatDuration(ms) {
158
+ if (!ms) return '0s';
159
+ if (ms < 1000) return ms + 'ms';
160
+ const s = Math.round(ms / 1000);
161
+ return s < 60 ? s + 's' : Math.floor(s/60) + 'm ' + (s%60) + 's';
162
+ }
163
+
164
+ function setFilter(f) {
165
+ filter = f;
166
+ document.querySelectorAll('.controls button').forEach(b => b.classList.remove('active'));
167
+ event.target.classList.add('active');
168
+ render();
169
+ }
170
+
171
+ function toggleTheme() {
172
+ isDark = !isDark;
173
+ document.body.classList.toggle('light', !isDark);
174
+ localStorage.setItem('aicc-theme', isDark ? 'dark' : 'light');
175
+ }
176
+
177
+ // Load saved theme
178
+ if (localStorage.getItem('aicc-theme') === 'light') { isDark = false; document.body.classList.add('light'); }
179
+
180
+ fetchPipelines();
181
+ setInterval(fetchPipelines, 5000);
182
+
183
+ // WebSocket for live updates
184
+ try {
185
+ const ws = new WebSocket(`ws://${location.host}`);
186
+ ws.onmessage = (e) => {
187
+ try {
188
+ const data = JSON.parse(e.data);
189
+ if (data.type === 'pipeline-event') fetchPipelines();
190
+ } catch {}
191
+ };
192
+ } catch {}
193
+ </script>
194
+ </body>
195
+ </html>