antigravity-ai-kit 2.1.0 → 3.0.1

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 (114) hide show
  1. package/.agent/README.md +4 -4
  2. package/.agent/agents/README.md +16 -12
  3. package/.agent/agents/architect.md +1 -0
  4. package/.agent/agents/backend-specialist.md +11 -0
  5. package/.agent/agents/code-reviewer.md +1 -0
  6. package/.agent/agents/database-architect.md +11 -0
  7. package/.agent/agents/devops-engineer.md +11 -0
  8. package/.agent/agents/e2e-runner.md +1 -0
  9. package/.agent/agents/explorer-agent.md +11 -0
  10. package/.agent/agents/frontend-specialist.md +11 -0
  11. package/.agent/agents/mobile-developer.md +11 -0
  12. package/.agent/agents/performance-optimizer.md +11 -0
  13. package/.agent/agents/planner.md +1 -0
  14. package/.agent/agents/refactor-cleaner.md +1 -0
  15. package/.agent/agents/reliability-engineer.md +11 -0
  16. package/.agent/agents/security-reviewer.md +1 -0
  17. package/.agent/agents/sprint-orchestrator.md +10 -0
  18. package/.agent/agents/tdd-guide.md +1 -0
  19. package/.agent/commands/code-review.md +1 -0
  20. package/.agent/commands/debug.md +1 -0
  21. package/.agent/commands/deploy.md +1 -0
  22. package/.agent/commands/help.md +252 -31
  23. package/.agent/commands/plan.md +1 -0
  24. package/.agent/commands/status.md +1 -0
  25. package/.agent/commands/tdd.md +1 -0
  26. package/.agent/contexts/brainstorm.md +26 -0
  27. package/.agent/contexts/debug.md +28 -0
  28. package/.agent/contexts/implement.md +29 -0
  29. package/.agent/contexts/review.md +27 -0
  30. package/.agent/contexts/ship.md +28 -0
  31. package/.agent/engine/identity.json +13 -0
  32. package/.agent/engine/loading-rules.json +23 -1
  33. package/.agent/engine/marketplace-index.json +29 -0
  34. package/.agent/engine/reliability-config.json +14 -0
  35. package/.agent/engine/sdlc-map.json +44 -0
  36. package/.agent/engine/workflow-state.json +28 -2
  37. package/.agent/hooks/hooks.json +27 -25
  38. package/.agent/manifest.json +12 -4
  39. package/.agent/rules.md +2 -1
  40. package/.agent/skills/README.md +10 -5
  41. package/.agent/skills/i18n-localization/SKILL.md +191 -0
  42. package/.agent/skills/mcp-integration/SKILL.md +224 -0
  43. package/.agent/skills/parallel-agents/SKILL.md +1 -1
  44. package/.agent/skills/shell-conventions/SKILL.md +92 -0
  45. package/.agent/skills/ui-ux-pro-max/SKILL.md +557 -0
  46. package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
  47. package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
  48. package/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
  49. package/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
  50. package/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
  51. package/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  52. package/.agent/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  53. package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  54. package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  55. package/.agent/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  56. package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  57. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  58. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  59. package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  60. package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  61. package/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  62. package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  63. package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  64. package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  65. package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -0
  66. package/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
  67. package/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  68. package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  69. package/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  70. package/.agent/skills/ui-ux-pro-max/scripts/core.py +253 -0
  71. package/.agent/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  72. package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -0
  73. package/.agent/templates/adr-template.md +32 -0
  74. package/.agent/templates/bug-report.md +37 -0
  75. package/.agent/templates/feature-request.md +32 -0
  76. package/.agent/workflows/README.md +92 -78
  77. package/.agent/workflows/brainstorm.md +154 -100
  78. package/.agent/workflows/create.md +142 -75
  79. package/.agent/workflows/debug.md +157 -98
  80. package/.agent/workflows/deploy.md +195 -144
  81. package/.agent/workflows/enhance.md +157 -65
  82. package/.agent/workflows/orchestrate.md +171 -114
  83. package/.agent/workflows/plan.md +147 -72
  84. package/.agent/workflows/preview.md +140 -83
  85. package/.agent/workflows/quality-gate.md +196 -0
  86. package/.agent/workflows/retrospective.md +197 -0
  87. package/.agent/workflows/review.md +188 -0
  88. package/.agent/workflows/status.md +142 -91
  89. package/.agent/workflows/test.md +168 -95
  90. package/.agent/workflows/ui-ux-pro-max.md +181 -127
  91. package/README.md +215 -78
  92. package/bin/ag-kit.js +344 -10
  93. package/lib/agent-registry.js +214 -0
  94. package/lib/agent-reputation.js +351 -0
  95. package/lib/cli-commands.js +235 -0
  96. package/lib/conflict-detector.js +245 -0
  97. package/lib/engineering-manager.js +354 -0
  98. package/lib/error-budget.js +294 -0
  99. package/lib/hook-system.js +252 -0
  100. package/lib/identity.js +245 -0
  101. package/lib/loading-engine.js +208 -0
  102. package/lib/marketplace.js +298 -0
  103. package/lib/plugin-system.js +604 -0
  104. package/lib/security-scanner.js +309 -0
  105. package/lib/self-healing.js +434 -0
  106. package/lib/session-manager.js +261 -0
  107. package/lib/skill-sandbox.js +244 -0
  108. package/lib/task-governance.js +523 -0
  109. package/lib/task-model.js +317 -0
  110. package/lib/updater.js +201 -0
  111. package/lib/verify.js +240 -0
  112. package/lib/workflow-engine.js +353 -0
  113. package/lib/workflow-persistence.js +160 -0
  114. package/package.json +7 -3
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Antigravity AI Kit — Agent Reputation Scoring
3
+ *
4
+ * Tracks agent task outcomes and computes reputation scores
5
+ * using a weighted formula with time-decay and cold-start bonus.
6
+ *
7
+ * @module lib/agent-reputation
8
+ * @author Emre Dursun
9
+ * @since v3.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const crypto = require('crypto');
17
+
18
+ const AGENT_DIR = '.agent';
19
+ const ENGINE_DIR = 'engine';
20
+ const REPUTATION_FILE = 'reputation.json';
21
+
22
+ /** Score bounds */
23
+ const MIN_SCORE = 0;
24
+ const MAX_SCORE = 1000;
25
+
26
+ /** Scoring weights */
27
+ const COMPLETION_WEIGHT = 10;
28
+ const FAILURE_WEIGHT = 15;
29
+
30
+ /** Cold-start threshold — bonus for agents with fewer than this many outcomes */
31
+ const COLD_START_THRESHOLD = 3;
32
+ const COLD_START_BONUS = 250;
33
+
34
+ /** Default half-life in days for time decay */
35
+ const DEFAULT_HALF_LIFE_DAYS = 30;
36
+
37
+ /**
38
+ * @typedef {object} OutcomeRecord
39
+ * @property {string} id - Unique outcome ID
40
+ * @property {string} agent - Agent name
41
+ * @property {'success' | 'failure'} result - Outcome result
42
+ * @property {number} cycleTimeMs - Time to complete in milliseconds
43
+ * @property {string} taskId - Associated task ID
44
+ * @property {string} timestamp - ISO timestamp
45
+ */
46
+
47
+ /**
48
+ * @typedef {object} AgentReputation
49
+ * @property {string} agent - Agent name
50
+ * @property {number} score - Clamped reputation score [0, 1000]
51
+ * @property {number} completions - Total successful outcomes
52
+ * @property {number} failures - Total failed outcomes
53
+ * @property {number} avgCycleTimeMs - Average cycle time in milliseconds
54
+ * @property {string | null} lastActive - ISO timestamp of last outcome
55
+ * @property {string} trend - Trend indicator: '↑', '↓', or '→'
56
+ * @property {number} reliability - Reliability percentage (0-100)
57
+ */
58
+
59
+ /**
60
+ * Resolves the reputation file path.
61
+ *
62
+ * @param {string} projectRoot - Root directory of the project
63
+ * @returns {string}
64
+ */
65
+ function resolveReputationPath(projectRoot) {
66
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, REPUTATION_FILE);
67
+ }
68
+
69
+ /**
70
+ * Loads the reputation data from disk.
71
+ *
72
+ * @param {string} projectRoot - Root directory
73
+ * @returns {{ outcomes: OutcomeRecord[], lastDecayed: string | null }}
74
+ */
75
+ function loadReputationData(projectRoot) {
76
+ const filePath = resolveReputationPath(projectRoot);
77
+
78
+ if (!fs.existsSync(filePath)) {
79
+ return { outcomes: [], lastDecayed: null };
80
+ }
81
+
82
+ try {
83
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
84
+ } catch {
85
+ return { outcomes: [], lastDecayed: null };
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Writes reputation data to disk atomically.
91
+ *
92
+ * @param {string} projectRoot - Root directory
93
+ * @param {{ outcomes: OutcomeRecord[], lastDecayed: string | null }} data
94
+ * @returns {void}
95
+ */
96
+ function writeReputationData(projectRoot, data) {
97
+ const filePath = resolveReputationPath(projectRoot);
98
+ const dir = path.dirname(filePath);
99
+
100
+ if (!fs.existsSync(dir)) {
101
+ fs.mkdirSync(dir, { recursive: true });
102
+ }
103
+
104
+ const tempPath = `${filePath}.tmp`;
105
+ fs.writeFileSync(tempPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
106
+ fs.renameSync(tempPath, filePath);
107
+ }
108
+
109
+ /**
110
+ * Clamps a value between min and max.
111
+ *
112
+ * @param {number} value - Value to clamp
113
+ * @param {number} min - Minimum
114
+ * @param {number} max - Maximum
115
+ * @returns {number}
116
+ */
117
+ function clamp(value, min, max) {
118
+ return Math.max(min, Math.min(max, value));
119
+ }
120
+
121
+ /**
122
+ * Records an agent task outcome.
123
+ *
124
+ * @param {string} projectRoot - Root directory
125
+ * @param {object} params - Outcome parameters
126
+ * @param {string} params.agent - Agent name
127
+ * @param {'success' | 'failure'} params.result - Outcome result
128
+ * @param {number} [params.cycleTimeMs] - Cycle time in ms (default: 0)
129
+ * @param {string} [params.taskId] - Associated task ID
130
+ * @returns {OutcomeRecord}
131
+ */
132
+ function recordOutcome(projectRoot, { agent, result, cycleTimeMs, taskId }) {
133
+ if (!agent || typeof agent !== 'string') {
134
+ throw new Error('Agent name is required');
135
+ }
136
+ if (!['success', 'failure'].includes(result)) {
137
+ throw new Error(`Invalid result: ${result}. Must be 'success' or 'failure'`);
138
+ }
139
+
140
+ const data = loadReputationData(projectRoot);
141
+
142
+ /** @type {OutcomeRecord} */
143
+ const record = {
144
+ id: `OUT-${crypto.randomUUID().slice(0, 8).toUpperCase()}`,
145
+ agent,
146
+ result,
147
+ cycleTimeMs: cycleTimeMs || 0,
148
+ taskId: taskId || 'unknown',
149
+ timestamp: new Date().toISOString(),
150
+ };
151
+
152
+ data.outcomes.push(record);
153
+ writeReputationData(projectRoot, data);
154
+
155
+ return record;
156
+ }
157
+
158
+ /**
159
+ * Calculates the consistency bonus based on outcome streak.
160
+ * Rewards agents with consecutive successes.
161
+ *
162
+ * @param {OutcomeRecord[]} agentOutcomes - Sorted outcomes for an agent
163
+ * @returns {number} Consistency bonus (0-100)
164
+ */
165
+ function calculateConsistencyBonus(agentOutcomes) {
166
+ if (agentOutcomes.length === 0) {
167
+ return 0;
168
+ }
169
+
170
+ // Count consecutive successes from most recent.
171
+ // Use original index as a stable tiebreaker when timestamps are identical
172
+ // (prevents non-deterministic sort across JS engines).
173
+ let streak = 0;
174
+ const indexed = agentOutcomes.map((o, i) => ({ ...o, _idx: i }));
175
+ const sorted = indexed.sort((a, b) => {
176
+ const timeDiff = new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
177
+ return timeDiff !== 0 ? timeDiff : b._idx - a._idx;
178
+ });
179
+
180
+ for (const outcome of sorted) {
181
+ if (outcome.result === 'success') {
182
+ streak += 1;
183
+ } else {
184
+ break;
185
+ }
186
+ }
187
+
188
+ // Cap bonus at 100 (10 consecutive successes)
189
+ return Math.min(streak * 10, 100);
190
+ }
191
+
192
+ /**
193
+ * Computes reputation for a single agent.
194
+ *
195
+ * @param {string} projectRoot - Root directory
196
+ * @param {string} agentName - Agent name
197
+ * @returns {AgentReputation}
198
+ */
199
+ function getReputation(projectRoot, agentName) {
200
+ const data = loadReputationData(projectRoot);
201
+ const agentOutcomes = data.outcomes.filter((o) => o.agent === agentName);
202
+
203
+ const completions = agentOutcomes.filter((o) => o.result === 'success').length;
204
+ const failures = agentOutcomes.filter((o) => o.result === 'failure').length;
205
+ const totalOutcomes = agentOutcomes.length;
206
+
207
+ // Average cycle time
208
+ const successOutcomes = agentOutcomes.filter((o) => o.result === 'success' && o.cycleTimeMs > 0);
209
+ const avgCycleTimeMs = successOutcomes.length > 0
210
+ ? Math.round(successOutcomes.reduce((sum, o) => sum + o.cycleTimeMs, 0) / successOutcomes.length)
211
+ : 0;
212
+
213
+ // Last active
214
+ const lastActive = agentOutcomes.length > 0
215
+ ? agentOutcomes.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())[0].timestamp
216
+ : null;
217
+
218
+ // Consistency bonus
219
+ const consistencyBonus = calculateConsistencyBonus(agentOutcomes);
220
+
221
+ // Cold-start bonus — only for agents with at least 1 but fewer than threshold outcomes
222
+ const coldStartBonus = (totalOutcomes > 0 && totalOutcomes < COLD_START_THRESHOLD) ? COLD_START_BONUS : 0;
223
+
224
+ // Raw score
225
+ const rawScore = (completions * COMPLETION_WEIGHT) - (failures * FAILURE_WEIGHT) + consistencyBonus + coldStartBonus;
226
+
227
+ // Clamped score
228
+ const score = clamp(Math.round(rawScore), MIN_SCORE, MAX_SCORE);
229
+
230
+ // Trend — compare last 5 vs previous 5
231
+ const trend = calculateTrend(agentOutcomes);
232
+
233
+ // Reliability percentage
234
+ const reliability = totalOutcomes > 0 ? Math.round((completions / totalOutcomes) * 100) : 0;
235
+
236
+ return {
237
+ agent: agentName,
238
+ score,
239
+ completions,
240
+ failures,
241
+ avgCycleTimeMs,
242
+ lastActive,
243
+ trend,
244
+ reliability,
245
+ };
246
+ }
247
+
248
+ /**
249
+ * Calculates trend from recent outcomes.
250
+ *
251
+ * @param {OutcomeRecord[]} outcomes - Agent outcomes
252
+ * @returns {string} '↑', '↓', or '→'
253
+ */
254
+ function calculateTrend(outcomes) {
255
+ if (outcomes.length < 2) {
256
+ return '→';
257
+ }
258
+
259
+ const sorted = [...outcomes].sort(
260
+ (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
261
+ );
262
+
263
+ const recentFive = sorted.slice(0, 5);
264
+ const previousFive = sorted.slice(5, 10);
265
+
266
+ if (previousFive.length === 0) {
267
+ return '→';
268
+ }
269
+
270
+ const recentSuccessRate = recentFive.filter((o) => o.result === 'success').length / recentFive.length;
271
+ const previousSuccessRate = previousFive.filter((o) => o.result === 'success').length / previousFive.length;
272
+
273
+ const delta = recentSuccessRate - previousSuccessRate;
274
+
275
+ if (delta > 0.1) {
276
+ return '↑';
277
+ }
278
+ if (delta < -0.1) {
279
+ return '↓';
280
+ }
281
+ return '→';
282
+ }
283
+
284
+ /**
285
+ * Returns all agents ranked by reputation score (descending).
286
+ * Agents with fewer than COLD_START_THRESHOLD outcomes are marked as 'new'.
287
+ *
288
+ * @param {string} projectRoot - Root directory
289
+ * @returns {AgentReputation[]}
290
+ */
291
+ function getRankings(projectRoot) {
292
+ const data = loadReputationData(projectRoot);
293
+
294
+ // Get unique agent names
295
+ const agentNames = [...new Set(data.outcomes.map((o) => o.agent))];
296
+
297
+ const rankings = agentNames.map((name) => getReputation(projectRoot, name));
298
+
299
+ // Sort by score descending, then by completions descending
300
+ rankings.sort((a, b) => {
301
+ if (b.score !== a.score) {
302
+ return b.score - a.score;
303
+ }
304
+ return b.completions - a.completions;
305
+ });
306
+
307
+ return rankings;
308
+ }
309
+
310
+ /**
311
+ * Applies time-decay to all outcome records.
312
+ * Removes outcomes older than 2× half-life to keep data manageable.
313
+ *
314
+ * @param {string} projectRoot - Root directory
315
+ * @param {object} [options] - Decay options
316
+ * @param {number} [options.halfLifeDays] - Half-life in days (default: 30)
317
+ * @returns {{ decayed: number, removed: number, remaining: number }}
318
+ */
319
+ function decayScores(projectRoot, options = {}) {
320
+ const halfLifeDays = options.halfLifeDays || DEFAULT_HALF_LIFE_DAYS;
321
+ const maxAgeDays = halfLifeDays * 2;
322
+ const now = Date.now();
323
+ const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000;
324
+
325
+ const data = loadReputationData(projectRoot);
326
+ const originalCount = data.outcomes.length;
327
+
328
+ // Remove outcomes older than 2× half-life
329
+ data.outcomes = data.outcomes.filter((outcome) => {
330
+ const ageMs = now - new Date(outcome.timestamp).getTime();
331
+ return ageMs < maxAgeMs;
332
+ });
333
+
334
+ const removedCount = originalCount - data.outcomes.length;
335
+
336
+ data.lastDecayed = new Date().toISOString();
337
+ writeReputationData(projectRoot, data);
338
+
339
+ return {
340
+ decayed: originalCount,
341
+ removed: removedCount,
342
+ remaining: data.outcomes.length,
343
+ };
344
+ }
345
+
346
+ module.exports = {
347
+ recordOutcome,
348
+ getReputation,
349
+ getRankings,
350
+ decayScores,
351
+ };
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Antigravity AI Kit — CLI Command Handlers (Phase 4)
3
+ *
4
+ * Extracted command handlers to keep bin/ag-kit.js under 800 lines.
5
+ * Handles: `market`, `heal`, and dashboard sections.
6
+ *
7
+ * @module lib/cli-commands
8
+ * @author Emre Dursun
9
+ * @since v3.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ // ANSI colors (shared with ag-kit.js)
18
+ const colors = {
19
+ reset: '\x1b[0m',
20
+ bright: '\x1b[1m',
21
+ green: '\x1b[32m',
22
+ blue: '\x1b[34m',
23
+ yellow: '\x1b[33m',
24
+ red: '\x1b[31m',
25
+ cyan: '\x1b[36m',
26
+ };
27
+
28
+ /**
29
+ * Marketplace CLI handler.
30
+ *
31
+ * @param {string} projectRoot - Root directory
32
+ * @param {string} subCommand - Sub-command: search, info, install, update
33
+ * @param {string} [argument] - Argument for the sub-command
34
+ * @param {object} [options] - CLI options
35
+ * @returns {void}
36
+ */
37
+ function marketCommand(projectRoot, subCommand, argument, options = {}) {
38
+ const marketplace = require('./marketplace');
39
+
40
+ switch (subCommand) {
41
+ case 'search': {
42
+ if (!argument) {
43
+ console.log(`${colors.yellow}Usage: ag-kit market search <query>${colors.reset}`);
44
+ return;
45
+ }
46
+ const results = marketplace.searchMarket(projectRoot, argument);
47
+ console.log(`\n${colors.bright}${colors.blue}═══ Marketplace Search: "${argument}" ═══${colors.reset}\n`);
48
+ if (results.length === 0) {
49
+ console.log(` No plugins found matching "${argument}"`);
50
+ } else {
51
+ for (const entry of results) {
52
+ console.log(` ${colors.green}${entry.name}${colors.reset} v${entry.version}`);
53
+ console.log(` ${entry.description}`);
54
+ console.log(` Tags: ${(entry.tags || []).join(', ')}`);
55
+ console.log('');
56
+ }
57
+ }
58
+ console.log(` ${results.length} result(s)\n`);
59
+ break;
60
+ }
61
+
62
+ case 'info': {
63
+ if (!argument) {
64
+ console.log(`${colors.yellow}Usage: ag-kit market info <plugin-name>${colors.reset}`);
65
+ return;
66
+ }
67
+ const info = marketplace.getMarketInfo(projectRoot, argument);
68
+ if (!info) {
69
+ console.log(`${colors.red}Plugin not found: ${argument}${colors.reset}`);
70
+ return;
71
+ }
72
+ console.log(`\n${colors.bright}${colors.blue}═══ Plugin: ${info.name} ═══${colors.reset}\n`);
73
+ console.log(` Version: ${info.version}`);
74
+ console.log(` Author: ${info.author}`);
75
+ console.log(` Description: ${info.description}`);
76
+ console.log(` Repository: ${info.repository}`);
77
+ console.log(` Tags: ${(info.tags || []).join(', ')}\n`);
78
+ break;
79
+ }
80
+
81
+ case 'install': {
82
+ if (!argument) {
83
+ console.log(`${colors.yellow}Usage: ag-kit market install <plugin-name>${colors.reset}`);
84
+ return;
85
+ }
86
+ console.log(`${colors.cyan}Installing ${argument}...${colors.reset}`);
87
+ const result = marketplace.installFromMarket(projectRoot, argument);
88
+ if (result.success) {
89
+ console.log(`${colors.green}✓ ${result.message}${colors.reset}`);
90
+ } else {
91
+ console.log(`${colors.red}✗ ${result.message}${colors.reset}`);
92
+ }
93
+ break;
94
+ }
95
+
96
+ case 'update': {
97
+ const force = options.force || false;
98
+ const result = marketplace.updateRegistryIndex(projectRoot, { force });
99
+ if (result.updated) {
100
+ console.log(`${colors.green}✓ Registry updated (${result.entryCount} entries)${colors.reset}`);
101
+ } else {
102
+ console.log(`${colors.yellow}Registry is fresh (${result.entryCount} entries). Use --force to update.${colors.reset}`);
103
+ }
104
+ break;
105
+ }
106
+
107
+ default:
108
+ console.log(`${colors.yellow}Usage: ag-kit market <search|info|install|update> [arg]${colors.reset}`);
109
+ break;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Self-healing CLI handler.
115
+ *
116
+ * @param {string} projectRoot - Root directory
117
+ * @param {object} [options] - CLI options
118
+ * @param {string} [options.file] - Path to CI log file
119
+ * @param {boolean} [options.apply] - Apply patches (default: dry-run)
120
+ * @returns {void}
121
+ */
122
+ function healCommand(projectRoot, options = {}) {
123
+ const selfHealing = require('./self-healing');
124
+ let ciOutput = '';
125
+
126
+ // Input source (E-3): file > stdin > last-saved
127
+ if (options.file) {
128
+ const filePath = path.resolve(options.file);
129
+ if (!fs.existsSync(filePath)) {
130
+ console.log(`${colors.red}✗ CI log file not found: ${filePath}${colors.reset}`);
131
+ process.exit(1);
132
+ }
133
+ ciOutput = fs.readFileSync(filePath, 'utf-8');
134
+ } else {
135
+ // Try last-saved CI output
136
+ const lastCiPath = path.join(projectRoot, '.agent', 'engine', 'last-ci-output.txt');
137
+ if (fs.existsSync(lastCiPath)) {
138
+ ciOutput = fs.readFileSync(lastCiPath, 'utf-8');
139
+ } else {
140
+ console.log(`${colors.yellow}No CI output found. Use: ag-kit heal --file <path>${colors.reset}`);
141
+ return;
142
+ }
143
+ }
144
+
145
+ console.log(`\n${colors.bright}${colors.blue}═══ Self-Healing Pipeline ═══${colors.reset}\n`);
146
+
147
+ // Detect failures
148
+ const failures = selfHealing.detectFailure(ciOutput);
149
+
150
+ if (failures.length === 0) {
151
+ console.log(` ${colors.green}✓ No failures detected — pipeline is healthy${colors.reset}\n`);
152
+ return;
153
+ }
154
+
155
+ console.log(` ${colors.red}${failures.length} failure(s) detected${colors.reset}\n`);
156
+
157
+ // Diagnose and generate patches
158
+ for (const failure of failures) {
159
+ const diagnosis = selfHealing.diagnoseFailure(failure);
160
+ console.log(` ${colors.yellow}[${failure.type.toUpperCase()}]${colors.reset} ${failure.message}`);
161
+ console.log(` Diagnosis: ${diagnosis.category} — ${diagnosis.explanation}`);
162
+
163
+ if (diagnosis.autoFixable) {
164
+ const patch = selfHealing.generateFixPatch(failure, diagnosis);
165
+ if (patch) {
166
+ const dryRun = !options.apply;
167
+ const result = selfHealing.applyFixWithConfirmation(projectRoot, patch, { dryRun });
168
+ console.log(` Patch ${patch.patchId}: ${dryRun ? 'DRY RUN' : result.applied ? 'APPLIED' : 'FAILED'}`);
169
+ }
170
+ } else {
171
+ console.log(` ${colors.cyan}→ Manual review required${colors.reset}`);
172
+ }
173
+ console.log('');
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Dashboard sections for Phase 4 features.
179
+ * Called by statusCommand() in ag-kit.js.
180
+ *
181
+ * @param {string} projectRoot - Root directory
182
+ * @returns {void}
183
+ */
184
+ function renderDashboardSections(projectRoot) {
185
+ // ═══ Reputation ═══
186
+ try {
187
+ const reputation = require('./agent-reputation');
188
+ const rankings = reputation.getRankings(projectRoot);
189
+
190
+ console.log(`\n${colors.bright}${colors.cyan}═══ Reputation ═══${colors.reset}`);
191
+
192
+ if (rankings.length === 0) {
193
+ console.log(' No reputation data yet');
194
+ } else {
195
+ for (const agent of rankings.slice(0, 5)) {
196
+ const trend = agent.trend;
197
+ console.log(` ${colors.green}${agent.agent}${colors.reset} — Score: ${agent.score} ${trend} | Reliability: ${agent.reliability}%`);
198
+ }
199
+ }
200
+ } catch {
201
+ // Reputation module not available — silently skip
202
+ }
203
+
204
+ // ═══ Sprint ═══
205
+ try {
206
+ const engMgr = require('./engineering-manager');
207
+ const metrics = engMgr.getSprintMetrics(projectRoot);
208
+
209
+ console.log(`\n${colors.bright}${colors.cyan}═══ Sprint ═══${colors.reset}`);
210
+ console.log(` Total Sprints: ${metrics.totalSprints}`);
211
+ console.log(` Active Sprint: ${metrics.activeSprint ? metrics.activeSprint.name : 'none'}`);
212
+ console.log(` Velocity: ${metrics.velocity} tasks/sprint`);
213
+ } catch {
214
+ // Module not available — silently skip
215
+ }
216
+
217
+ // ═══ Health ═══
218
+ try {
219
+ const selfHealing = require('./self-healing');
220
+ const report = selfHealing.getHealingReport(projectRoot);
221
+
222
+ console.log(`\n${colors.bright}${colors.cyan}═══ Health ═══${colors.reset}`);
223
+ console.log(` Total Heals: ${report.totalHeals}`);
224
+ console.log(` Success Rate: ${report.successRate}%`);
225
+ console.log(` Pending Patches: ${report.pendingPatches}`);
226
+ } catch {
227
+ // Module not available — silently skip
228
+ }
229
+ }
230
+
231
+ module.exports = {
232
+ marketCommand,
233
+ healCommand,
234
+ renderDashboardSections,
235
+ };