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
package/lib/identity.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity AI Kit — Developer Identity System
|
|
3
|
+
*
|
|
4
|
+
* Local identity management for multi-developer scenarios.
|
|
5
|
+
* Auto-detects from git config with manual registration fallback.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/identity
|
|
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
|
+
const { execSync } = require('child_process');
|
|
18
|
+
|
|
19
|
+
const AGENT_DIR = '.agent';
|
|
20
|
+
const ENGINE_DIR = 'engine';
|
|
21
|
+
const IDENTITY_FILE = 'identity.json';
|
|
22
|
+
|
|
23
|
+
/** @type {readonly string[]} */
|
|
24
|
+
const VALID_ROLES = ['owner', 'contributor', 'reviewer'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} Identity
|
|
28
|
+
* @property {string} id - Deterministic developer ID (SHA-256 truncated)
|
|
29
|
+
* @property {string} name - Developer name
|
|
30
|
+
* @property {string} email - Developer email
|
|
31
|
+
* @property {string} role - Developer role
|
|
32
|
+
* @property {string} registeredAt - ISO timestamp
|
|
33
|
+
* @property {string} lastActiveAt - ISO timestamp
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolves the identity file path.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} projectRoot - Root directory of the project
|
|
40
|
+
* @returns {string} Absolute path to identity.json
|
|
41
|
+
*/
|
|
42
|
+
function resolveIdentityPath(projectRoot) {
|
|
43
|
+
return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, IDENTITY_FILE);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generates a deterministic developer ID from email.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} email - Developer email
|
|
50
|
+
* @returns {string} 12-char hex ID
|
|
51
|
+
*/
|
|
52
|
+
function generateDeveloperId(email) {
|
|
53
|
+
return crypto
|
|
54
|
+
.createHash('sha256')
|
|
55
|
+
.update(email.toLowerCase().trim())
|
|
56
|
+
.digest('hex')
|
|
57
|
+
.slice(0, 12);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Loads the identity registry from disk.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} projectRoot - Root directory of the project
|
|
64
|
+
* @returns {{ developers: Identity[], activeId: string | null }}
|
|
65
|
+
*/
|
|
66
|
+
function loadIdentityRegistry(projectRoot) {
|
|
67
|
+
const identityPath = resolveIdentityPath(projectRoot);
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(identityPath)) {
|
|
70
|
+
return { developers: [], activeId: null };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(fs.readFileSync(identityPath, 'utf-8'));
|
|
75
|
+
} catch {
|
|
76
|
+
return { developers: [], activeId: null };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Writes the identity registry to disk atomically.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} projectRoot - Root directory of the project
|
|
84
|
+
* @param {object} registry - Registry data
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
function writeIdentityRegistry(projectRoot, registry) {
|
|
88
|
+
const identityPath = resolveIdentityPath(projectRoot);
|
|
89
|
+
const tempPath = `${identityPath}.tmp`;
|
|
90
|
+
const dir = path.dirname(identityPath);
|
|
91
|
+
|
|
92
|
+
if (!fs.existsSync(dir)) {
|
|
93
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(tempPath, JSON.stringify(registry, null, 2) + '\n', 'utf-8');
|
|
97
|
+
fs.renameSync(tempPath, identityPath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Auto-detects developer identity from git config.
|
|
102
|
+
*
|
|
103
|
+
* @returns {{ name: string, email: string } | null}
|
|
104
|
+
*/
|
|
105
|
+
function detectFromGit() {
|
|
106
|
+
try {
|
|
107
|
+
const name = execSync('git config user.name', { encoding: 'utf-8' }).trim();
|
|
108
|
+
const email = execSync('git config user.email', { encoding: 'utf-8' }).trim();
|
|
109
|
+
|
|
110
|
+
if (name && email) {
|
|
111
|
+
return { name, email };
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Registers a new developer identity.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} projectRoot - Root directory of the project
|
|
123
|
+
* @param {object} params - Identity parameters
|
|
124
|
+
* @param {string} params.name - Developer name
|
|
125
|
+
* @param {string} params.email - Developer email
|
|
126
|
+
* @param {string} [params.role] - Developer role (default: 'contributor')
|
|
127
|
+
* @returns {{ success: boolean, identity: Identity, isNew: boolean }}
|
|
128
|
+
*/
|
|
129
|
+
function registerIdentity(projectRoot, { name, email, role }) {
|
|
130
|
+
const registry = loadIdentityRegistry(projectRoot);
|
|
131
|
+
const developerId = generateDeveloperId(email);
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
const identityRole = role && VALID_ROLES.includes(role) ? role : 'contributor';
|
|
134
|
+
|
|
135
|
+
// Check if already registered
|
|
136
|
+
const existingIndex = registry.developers.findIndex((d) => d.id === developerId);
|
|
137
|
+
|
|
138
|
+
if (existingIndex !== -1) {
|
|
139
|
+
// Update last active
|
|
140
|
+
registry.developers[existingIndex].lastActiveAt = now;
|
|
141
|
+
registry.developers[existingIndex].name = name;
|
|
142
|
+
registry.activeId = developerId;
|
|
143
|
+
writeIdentityRegistry(projectRoot, registry);
|
|
144
|
+
return { success: true, identity: registry.developers[existingIndex], isNew: false };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @type {Identity} */
|
|
148
|
+
const identity = {
|
|
149
|
+
id: developerId,
|
|
150
|
+
name,
|
|
151
|
+
email: email.toLowerCase().trim(),
|
|
152
|
+
role: identityRole,
|
|
153
|
+
registeredAt: now,
|
|
154
|
+
lastActiveAt: now,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
registry.developers.push(identity);
|
|
158
|
+
|
|
159
|
+
// First developer becomes owner automatically
|
|
160
|
+
if (registry.developers.length === 1) {
|
|
161
|
+
identity.role = 'owner';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
registry.activeId = developerId;
|
|
165
|
+
writeIdentityRegistry(projectRoot, registry);
|
|
166
|
+
|
|
167
|
+
return { success: true, identity, isNew: true };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Gets the current active developer identity.
|
|
172
|
+
* Auto-detects and registers from git if no identity exists.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} projectRoot - Root directory of the project
|
|
175
|
+
* @returns {Identity | null}
|
|
176
|
+
*/
|
|
177
|
+
function getCurrentIdentity(projectRoot) {
|
|
178
|
+
const registry = loadIdentityRegistry(projectRoot);
|
|
179
|
+
|
|
180
|
+
if (registry.activeId) {
|
|
181
|
+
const identity = registry.developers.find((d) => d.id === registry.activeId);
|
|
182
|
+
if (identity) {
|
|
183
|
+
return identity;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Auto-detect from git
|
|
188
|
+
const gitInfo = detectFromGit();
|
|
189
|
+
if (gitInfo) {
|
|
190
|
+
const result = registerIdentity(projectRoot, { name: gitInfo.name, email: gitInfo.email });
|
|
191
|
+
return result.identity;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validates an identity exists and is properly formed.
|
|
199
|
+
*
|
|
200
|
+
* @param {string} developerId - Developer ID to validate
|
|
201
|
+
* @param {string} projectRoot - Root directory of the project
|
|
202
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
203
|
+
*/
|
|
204
|
+
function validateIdentity(developerId, projectRoot) {
|
|
205
|
+
const registry = loadIdentityRegistry(projectRoot);
|
|
206
|
+
/** @type {string[]} */
|
|
207
|
+
const errors = [];
|
|
208
|
+
|
|
209
|
+
const identity = registry.developers.find((d) => d.id === developerId);
|
|
210
|
+
|
|
211
|
+
if (!identity) {
|
|
212
|
+
return { valid: false, errors: [`Identity not found: ${developerId}`] };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!identity.name || identity.name.trim().length === 0) {
|
|
216
|
+
errors.push('Identity missing name');
|
|
217
|
+
}
|
|
218
|
+
if (!identity.email || !identity.email.includes('@')) {
|
|
219
|
+
errors.push('Identity missing valid email');
|
|
220
|
+
}
|
|
221
|
+
if (!VALID_ROLES.includes(identity.role)) {
|
|
222
|
+
errors.push(`Invalid role: ${identity.role}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { valid: errors.length === 0, errors };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Lists all registered developer identities.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} projectRoot - Root directory of the project
|
|
232
|
+
* @returns {{ developers: Identity[], activeId: string | null }}
|
|
233
|
+
*/
|
|
234
|
+
function listIdentities(projectRoot) {
|
|
235
|
+
return loadIdentityRegistry(projectRoot);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
registerIdentity,
|
|
240
|
+
getCurrentIdentity,
|
|
241
|
+
validateIdentity,
|
|
242
|
+
listIdentities,
|
|
243
|
+
generateDeveloperId,
|
|
244
|
+
detectFromGit,
|
|
245
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity AI Kit — Loading Rules Engine
|
|
3
|
+
*
|
|
4
|
+
* Runtime implementation of loading-rules.json keyword matching
|
|
5
|
+
* and context budget enforcement.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/loading-engine
|
|
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
|
+
const AGENT_DIR = '.agent';
|
|
18
|
+
const ENGINE_DIR = 'engine';
|
|
19
|
+
const LOADING_RULES_FILE = 'loading-rules.json';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} LoadPlan
|
|
23
|
+
* @property {string[]} agents - Agents to load
|
|
24
|
+
* @property {string[]} skills - Skills to load
|
|
25
|
+
* @property {string[]} warnings - Budget or resolution warnings
|
|
26
|
+
* @property {object} budgetUsage - Context budget usage stats
|
|
27
|
+
* @property {number} budgetUsage.agentsUsed - Number of agents selected
|
|
28
|
+
* @property {number} budgetUsage.agentsMax - Maximum allowed
|
|
29
|
+
* @property {number} budgetUsage.skillsUsed - Number of skills selected
|
|
30
|
+
* @property {number} budgetUsage.skillsMax - Maximum allowed
|
|
31
|
+
* @property {string[]} matchedDomains - Which domains matched
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Loads and parses loading-rules.json.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} projectRoot - Root directory of the project
|
|
38
|
+
* @returns {object} Parsed loading rules
|
|
39
|
+
*/
|
|
40
|
+
function loadRules(projectRoot) {
|
|
41
|
+
const rulesPath = path.join(projectRoot, AGENT_DIR, ENGINE_DIR, LOADING_RULES_FILE);
|
|
42
|
+
|
|
43
|
+
if (!fs.existsSync(rulesPath)) {
|
|
44
|
+
throw new Error(`Loading rules not found: ${rulesPath}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return JSON.parse(fs.readFileSync(rulesPath, 'utf-8'));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Matches a task description against domain keywords.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} taskDescription - Human-readable task text
|
|
54
|
+
* @param {string} projectRoot - Root directory of the project
|
|
55
|
+
* @returns {{ matchedDomains: string[], agents: string[], skills: string[] }}
|
|
56
|
+
*/
|
|
57
|
+
function resolveForTask(taskDescription, projectRoot) {
|
|
58
|
+
const rules = loadRules(projectRoot);
|
|
59
|
+
const domainRules = rules.domainRules || [];
|
|
60
|
+
const lowerTask = taskDescription.toLowerCase();
|
|
61
|
+
|
|
62
|
+
/** @type {Set<string>} */
|
|
63
|
+
const agents = new Set();
|
|
64
|
+
/** @type {Set<string>} */
|
|
65
|
+
const skills = new Set();
|
|
66
|
+
/** @type {string[]} */
|
|
67
|
+
const matchedDomains = [];
|
|
68
|
+
|
|
69
|
+
for (const rule of domainRules) {
|
|
70
|
+
const hasMatch = (rule.keywords || []).some((keyword) => lowerTask.includes(keyword.toLowerCase()));
|
|
71
|
+
|
|
72
|
+
if (hasMatch) {
|
|
73
|
+
matchedDomains.push(rule.domain);
|
|
74
|
+
|
|
75
|
+
for (const agent of (rule.loadAgents || [])) {
|
|
76
|
+
agents.add(agent);
|
|
77
|
+
}
|
|
78
|
+
for (const skill of (rule.loadSkills || [])) {
|
|
79
|
+
skills.add(skill);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
matchedDomains,
|
|
86
|
+
agents: [...agents],
|
|
87
|
+
skills: [...skills],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Resolves agents and skills for a named workflow using workflow bindings.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} workflowName - Name of the workflow
|
|
95
|
+
* @param {string} projectRoot - Root directory of the project
|
|
96
|
+
* @returns {{ agents: string[], skills: string[], bindingType: string }}
|
|
97
|
+
*/
|
|
98
|
+
function resolveForWorkflow(workflowName, projectRoot) {
|
|
99
|
+
const rules = loadRules(projectRoot);
|
|
100
|
+
const bindings = rules.workflowBindings || [];
|
|
101
|
+
const match = bindings.find((b) => b.workflow === workflowName);
|
|
102
|
+
|
|
103
|
+
if (!match) {
|
|
104
|
+
return { agents: [], skills: [], bindingType: 'none' };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
agents: match.loadAgents || [],
|
|
109
|
+
skills: match.loadSkills || [],
|
|
110
|
+
bindingType: match.bindingType || 'inferred',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Enforces context budget limits by trimming agents and skills.
|
|
116
|
+
*
|
|
117
|
+
* @param {string[]} agents - Candidate agents
|
|
118
|
+
* @param {string[]} skills - Candidate skills
|
|
119
|
+
* @param {string} projectRoot - Root directory of the project
|
|
120
|
+
* @returns {{ agents: string[], skills: string[], trimmed: boolean, warnings: string[] }}
|
|
121
|
+
*/
|
|
122
|
+
function enforceContextBudget(agents, skills, projectRoot) {
|
|
123
|
+
const rules = loadRules(projectRoot);
|
|
124
|
+
const budget = rules.contextBudget || {};
|
|
125
|
+
const maxAgents = budget.maxAgentsPerSession || 4;
|
|
126
|
+
const maxSkills = budget.maxSkillsPerSession || 6;
|
|
127
|
+
const warningThreshold = (budget.warningThresholdPercent || 80) / 100;
|
|
128
|
+
|
|
129
|
+
/** @type {string[]} */
|
|
130
|
+
const warnings = [];
|
|
131
|
+
let trimmed = false;
|
|
132
|
+
|
|
133
|
+
let finalAgents = [...agents];
|
|
134
|
+
let finalSkills = [...skills];
|
|
135
|
+
|
|
136
|
+
if (finalAgents.length > maxAgents) {
|
|
137
|
+
warnings.push(`Agent budget exceeded: ${finalAgents.length}/${maxAgents} — trimmed to ${maxAgents}`);
|
|
138
|
+
finalAgents = finalAgents.slice(0, maxAgents);
|
|
139
|
+
trimmed = true;
|
|
140
|
+
} else if (finalAgents.length >= maxAgents * warningThreshold) {
|
|
141
|
+
warnings.push(`Agent budget near limit: ${finalAgents.length}/${maxAgents} (${Math.round(finalAgents.length / maxAgents * 100)}%)`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (finalSkills.length > maxSkills) {
|
|
145
|
+
warnings.push(`Skill budget exceeded: ${finalSkills.length}/${maxSkills} — trimmed to ${maxSkills}`);
|
|
146
|
+
finalSkills = finalSkills.slice(0, maxSkills);
|
|
147
|
+
trimmed = true;
|
|
148
|
+
} else if (finalSkills.length >= maxSkills * warningThreshold) {
|
|
149
|
+
warnings.push(`Skill budget near limit: ${finalSkills.length}/${maxSkills} (${Math.round(finalSkills.length / maxSkills * 100)}%)`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { agents: finalAgents, skills: finalSkills, trimmed, warnings };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Full resolution: combines domain matching, workflow binding, and budget enforcement.
|
|
157
|
+
*
|
|
158
|
+
* @param {string} taskDescription - Task text for domain matching
|
|
159
|
+
* @param {string} [workflowName] - Optional workflow name for binding resolution
|
|
160
|
+
* @param {string} projectRoot - Root directory of the project
|
|
161
|
+
* @returns {LoadPlan}
|
|
162
|
+
*/
|
|
163
|
+
function getLoadPlan(taskDescription, workflowName, projectRoot) {
|
|
164
|
+
const rules = loadRules(projectRoot);
|
|
165
|
+
const budget = rules.contextBudget || {};
|
|
166
|
+
|
|
167
|
+
// Step 1: Domain keyword matching
|
|
168
|
+
const taskResolution = resolveForTask(taskDescription, projectRoot);
|
|
169
|
+
|
|
170
|
+
// Step 2: Workflow bindings (if workflow specified)
|
|
171
|
+
/** @type {Set<string>} */
|
|
172
|
+
const allAgents = new Set(taskResolution.agents);
|
|
173
|
+
/** @type {Set<string>} */
|
|
174
|
+
const allSkills = new Set(taskResolution.skills);
|
|
175
|
+
|
|
176
|
+
if (workflowName) {
|
|
177
|
+
const wfResolution = resolveForWorkflow(workflowName, projectRoot);
|
|
178
|
+
for (const agent of wfResolution.agents) {
|
|
179
|
+
allAgents.add(agent);
|
|
180
|
+
}
|
|
181
|
+
for (const skill of wfResolution.skills) {
|
|
182
|
+
allSkills.add(skill);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Step 3: Budget enforcement
|
|
187
|
+
const budgetResult = enforceContextBudget([...allAgents], [...allSkills], projectRoot);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
agents: budgetResult.agents,
|
|
191
|
+
skills: budgetResult.skills,
|
|
192
|
+
warnings: budgetResult.warnings,
|
|
193
|
+
budgetUsage: {
|
|
194
|
+
agentsUsed: budgetResult.agents.length,
|
|
195
|
+
agentsMax: budget.maxAgentsPerSession || 4,
|
|
196
|
+
skillsUsed: budgetResult.skills.length,
|
|
197
|
+
skillsMax: budget.maxSkillsPerSession || 6,
|
|
198
|
+
},
|
|
199
|
+
matchedDomains: taskResolution.matchedDomains,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = {
|
|
204
|
+
resolveForTask,
|
|
205
|
+
resolveForWorkflow,
|
|
206
|
+
enforceContextBudget,
|
|
207
|
+
getLoadPlan,
|
|
208
|
+
};
|