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.
- package/.agent/README.md +4 -4
- package/.agent/agents/README.md +16 -12
- package/.agent/agents/architect.md +1 -0
- package/.agent/agents/backend-specialist.md +11 -0
- package/.agent/agents/code-reviewer.md +1 -0
- package/.agent/agents/database-architect.md +11 -0
- package/.agent/agents/devops-engineer.md +11 -0
- package/.agent/agents/e2e-runner.md +1 -0
- package/.agent/agents/explorer-agent.md +11 -0
- package/.agent/agents/frontend-specialist.md +11 -0
- package/.agent/agents/mobile-developer.md +11 -0
- package/.agent/agents/performance-optimizer.md +11 -0
- package/.agent/agents/planner.md +1 -0
- package/.agent/agents/refactor-cleaner.md +1 -0
- package/.agent/agents/reliability-engineer.md +11 -0
- package/.agent/agents/security-reviewer.md +1 -0
- package/.agent/agents/sprint-orchestrator.md +10 -0
- package/.agent/agents/tdd-guide.md +1 -0
- package/.agent/commands/code-review.md +1 -0
- package/.agent/commands/debug.md +1 -0
- package/.agent/commands/deploy.md +1 -0
- package/.agent/commands/help.md +252 -31
- package/.agent/commands/plan.md +1 -0
- package/.agent/commands/status.md +1 -0
- package/.agent/commands/tdd.md +1 -0
- package/.agent/contexts/brainstorm.md +26 -0
- package/.agent/contexts/debug.md +28 -0
- package/.agent/contexts/implement.md +29 -0
- package/.agent/contexts/review.md +27 -0
- package/.agent/contexts/ship.md +28 -0
- package/.agent/engine/identity.json +13 -0
- package/.agent/engine/loading-rules.json +23 -1
- package/.agent/engine/marketplace-index.json +29 -0
- package/.agent/engine/reliability-config.json +14 -0
- package/.agent/engine/sdlc-map.json +44 -0
- package/.agent/engine/workflow-state.json +28 -2
- package/.agent/hooks/hooks.json +27 -25
- package/.agent/manifest.json +12 -4
- package/.agent/rules.md +2 -1
- package/.agent/skills/README.md +10 -5
- package/.agent/skills/i18n-localization/SKILL.md +191 -0
- package/.agent/skills/mcp-integration/SKILL.md +224 -0
- package/.agent/skills/parallel-agents/SKILL.md +1 -1
- package/.agent/skills/shell-conventions/SKILL.md +92 -0
- package/.agent/skills/ui-ux-pro-max/SKILL.md +557 -0
- package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.agent/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.agent/templates/adr-template.md +32 -0
- package/.agent/templates/bug-report.md +37 -0
- package/.agent/templates/feature-request.md +32 -0
- package/.agent/workflows/README.md +92 -78
- package/.agent/workflows/brainstorm.md +154 -100
- package/.agent/workflows/create.md +142 -75
- package/.agent/workflows/debug.md +157 -98
- package/.agent/workflows/deploy.md +195 -144
- package/.agent/workflows/enhance.md +157 -65
- package/.agent/workflows/orchestrate.md +171 -114
- package/.agent/workflows/plan.md +147 -72
- package/.agent/workflows/preview.md +140 -83
- package/.agent/workflows/quality-gate.md +196 -0
- package/.agent/workflows/retrospective.md +197 -0
- package/.agent/workflows/review.md +188 -0
- package/.agent/workflows/status.md +142 -91
- package/.agent/workflows/test.md +168 -95
- package/.agent/workflows/ui-ux-pro-max.md +181 -127
- package/README.md +215 -78
- package/bin/ag-kit.js +344 -10
- package/lib/agent-registry.js +214 -0
- package/lib/agent-reputation.js +351 -0
- package/lib/cli-commands.js +235 -0
- package/lib/conflict-detector.js +245 -0
- package/lib/engineering-manager.js +354 -0
- package/lib/error-budget.js +294 -0
- package/lib/hook-system.js +252 -0
- package/lib/identity.js +245 -0
- package/lib/loading-engine.js +208 -0
- package/lib/marketplace.js +298 -0
- package/lib/plugin-system.js +604 -0
- package/lib/security-scanner.js +309 -0
- package/lib/self-healing.js +434 -0
- package/lib/session-manager.js +261 -0
- package/lib/skill-sandbox.js +244 -0
- package/lib/task-governance.js +523 -0
- package/lib/task-model.js +317 -0
- package/lib/updater.js +201 -0
- package/lib/verify.js +240 -0
- package/lib/workflow-engine.js +353 -0
- package/lib/workflow-persistence.js +160 -0
- 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
|
+
};
|