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,294 @@
1
+ /**
2
+ * Antigravity AI Kit — Error Budget Tracker
3
+ *
4
+ * Enables reliability-config.json error budget tracking with
5
+ * basic metrics collection. Records test, build, and deployment
6
+ * results and calculates budget health.
7
+ *
8
+ * @module lib/error-budget
9
+ * @author Emre Dursun
10
+ * @since v3.0.0
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const AGENT_DIR = '.agent';
19
+ const ENGINE_DIR = 'engine';
20
+ const RELIABILITY_CONFIG = 'reliability-config.json';
21
+ const METRICS_FILE = 'metrics.json';
22
+
23
+ /**
24
+ * @typedef {'HEALTHY' | 'WARNING' | 'EXHAUSTED'} BudgetStatus
25
+ */
26
+
27
+ /**
28
+ * @typedef {object} MetricsData
29
+ * @property {number} testsPassed - Total tests passed
30
+ * @property {number} testsFailed - Total tests failed
31
+ * @property {number} buildsSucceeded - Total successful builds
32
+ * @property {number} buildsFailed - Total failed builds
33
+ * @property {number} deploysSucceeded - Total successful deploys
34
+ * @property {number} deploysRolledBack - Total rolled-back deploys
35
+ * @property {string} periodStart - ISO timestamp of period start
36
+ * @property {string} lastUpdated - ISO timestamp of last update
37
+ */
38
+
39
+ /**
40
+ * @typedef {object} BudgetReport
41
+ * @property {BudgetStatus} status - Overall budget health
42
+ * @property {object} rates - Current failure rates
43
+ * @property {number} rates.testFailureRate - Test failure rate (%)
44
+ * @property {number} rates.buildFailureRate - Build failure rate (%)
45
+ * @property {number} rates.deployRollbackRate - Deploy rollback rate (%)
46
+ * @property {object} thresholds - Configured thresholds
47
+ * @property {number} thresholds.testFailureRatePercent - Max test failure rate
48
+ * @property {number} thresholds.buildFailureRatePercent - Max build failure rate
49
+ * @property {number} thresholds.deployRollbackRatePercent - Max deploy rollback rate
50
+ * @property {string[]} violations - Which metrics exceeded thresholds
51
+ */
52
+
53
+ /**
54
+ * Resolves the path to the metrics file.
55
+ *
56
+ * @param {string} projectRoot - Root directory of the project
57
+ * @returns {string} Absolute path to metrics.json
58
+ */
59
+ function resolveMetricsPath(projectRoot) {
60
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, METRICS_FILE);
61
+ }
62
+
63
+ /**
64
+ * Resolves the path to the reliability config.
65
+ *
66
+ * @param {string} projectRoot - Root directory of the project
67
+ * @returns {string} Absolute path to reliability-config.json
68
+ */
69
+ function resolveConfigPath(projectRoot) {
70
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, RELIABILITY_CONFIG);
71
+ }
72
+
73
+ /**
74
+ * Loads the reliability configuration.
75
+ *
76
+ * @param {string} projectRoot - Root directory of the project
77
+ * @returns {object} Parsed reliability config
78
+ */
79
+ function loadConfig(projectRoot) {
80
+ const configPath = resolveConfigPath(projectRoot);
81
+
82
+ if (!fs.existsSync(configPath)) {
83
+ throw new Error(`Reliability config not found: ${configPath}`);
84
+ }
85
+
86
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
87
+ }
88
+
89
+ /**
90
+ * Loads metrics data from disk, or returns defaults if file doesn't exist.
91
+ *
92
+ * @param {string} projectRoot - Root directory of the project
93
+ * @returns {MetricsData}
94
+ */
95
+ function loadMetrics(projectRoot) {
96
+ const metricsPath = resolveMetricsPath(projectRoot);
97
+
98
+ if (!fs.existsSync(metricsPath)) {
99
+ return createEmptyMetrics();
100
+ }
101
+
102
+ try {
103
+ return JSON.parse(fs.readFileSync(metricsPath, 'utf-8'));
104
+ } catch {
105
+ return createEmptyMetrics();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Creates an empty metrics object.
111
+ *
112
+ * @returns {MetricsData}
113
+ */
114
+ function createEmptyMetrics() {
115
+ return {
116
+ testsPassed: 0,
117
+ testsFailed: 0,
118
+ buildsSucceeded: 0,
119
+ buildsFailed: 0,
120
+ deploysSucceeded: 0,
121
+ deploysRolledBack: 0,
122
+ periodStart: new Date().toISOString(),
123
+ lastUpdated: new Date().toISOString(),
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Writes metrics data to disk atomically.
129
+ *
130
+ * @param {string} projectRoot - Root directory of the project
131
+ * @param {MetricsData} metrics - Metrics data to write
132
+ * @returns {void}
133
+ */
134
+ function writeMetrics(projectRoot, metrics) {
135
+ const metricsPath = resolveMetricsPath(projectRoot);
136
+ const tempPath = `${metricsPath}.tmp`;
137
+
138
+ metrics.lastUpdated = new Date().toISOString();
139
+
140
+ fs.writeFileSync(tempPath, JSON.stringify(metrics, null, 2) + '\n', 'utf-8');
141
+ fs.renameSync(tempPath, metricsPath);
142
+ }
143
+
144
+ /**
145
+ * Records the result of a test run.
146
+ *
147
+ * @param {string} projectRoot - Root directory of the project
148
+ * @param {number} passed - Number of tests passed
149
+ * @param {number} failed - Number of tests failed
150
+ * @returns {void}
151
+ */
152
+ function recordTestResult(projectRoot, passed, failed) {
153
+ const metrics = loadMetrics(projectRoot);
154
+ metrics.testsPassed += passed;
155
+ metrics.testsFailed += failed;
156
+ writeMetrics(projectRoot, metrics);
157
+ }
158
+
159
+ /**
160
+ * Records the result of a build.
161
+ *
162
+ * @param {string} projectRoot - Root directory of the project
163
+ * @param {boolean} success - Whether the build succeeded
164
+ * @returns {void}
165
+ */
166
+ function recordBuildResult(projectRoot, success) {
167
+ const metrics = loadMetrics(projectRoot);
168
+
169
+ if (success) {
170
+ metrics.buildsSucceeded += 1;
171
+ } else {
172
+ metrics.buildsFailed += 1;
173
+ }
174
+
175
+ writeMetrics(projectRoot, metrics);
176
+ }
177
+
178
+ /**
179
+ * Records the result of a deployment.
180
+ *
181
+ * @param {string} projectRoot - Root directory of the project
182
+ * @param {boolean} success - Whether deploy succeeded
183
+ * @param {boolean} [rolledBack=false] - Whether it was rolled back
184
+ * @returns {void}
185
+ */
186
+ function recordDeployResult(projectRoot, success, rolledBack = false) {
187
+ const metrics = loadMetrics(projectRoot);
188
+
189
+ if (success && !rolledBack) {
190
+ metrics.deploysSucceeded += 1;
191
+ }
192
+
193
+ if (rolledBack) {
194
+ metrics.deploysRolledBack += 1;
195
+ }
196
+
197
+ writeMetrics(projectRoot, metrics);
198
+ }
199
+
200
+ /**
201
+ * Calculates a failure rate percentage safely.
202
+ *
203
+ * @param {number} failed - Number of failures
204
+ * @param {number} total - Total attempts
205
+ * @returns {number} Failure rate as percentage (0-100)
206
+ */
207
+ function calculateRate(failed, total) {
208
+ if (total === 0) {
209
+ return 0;
210
+ }
211
+ return Number(((failed / total) * 100).toFixed(2));
212
+ }
213
+
214
+ /**
215
+ * Generates a budget health report.
216
+ *
217
+ * @param {string} projectRoot - Root directory of the project
218
+ * @returns {BudgetReport}
219
+ */
220
+ function getBudgetReport(projectRoot) {
221
+ const config = loadConfig(projectRoot);
222
+ const metrics = loadMetrics(projectRoot);
223
+ const thresholds = config.errorBudget?.thresholds || {};
224
+
225
+ const testTotal = metrics.testsPassed + metrics.testsFailed;
226
+ const buildTotal = metrics.buildsSucceeded + metrics.buildsFailed;
227
+ const deployTotal = metrics.deploysSucceeded + metrics.deploysRolledBack;
228
+
229
+ const rates = {
230
+ testFailureRate: calculateRate(metrics.testsFailed, testTotal),
231
+ buildFailureRate: calculateRate(metrics.buildsFailed, buildTotal),
232
+ deployRollbackRate: calculateRate(metrics.deploysRolledBack, deployTotal),
233
+ };
234
+
235
+ /** @type {string[]} */
236
+ const violations = [];
237
+
238
+ if (rates.testFailureRate > (thresholds.testFailureRatePercent || 5)) {
239
+ violations.push('testFailureRate');
240
+ }
241
+ if (rates.buildFailureRate > (thresholds.buildFailureRatePercent || 2)) {
242
+ violations.push('buildFailureRate');
243
+ }
244
+ if (rates.deployRollbackRate > (thresholds.deployRollbackRatePercent || 10)) {
245
+ violations.push('deployRollbackRate');
246
+ }
247
+
248
+ /** @type {BudgetStatus} */
249
+ let status = 'HEALTHY';
250
+
251
+ if (violations.length > 0) {
252
+ status = 'EXHAUSTED';
253
+ } else {
254
+ // Warning if any rate is above 80% of threshold
255
+ const testWarning = rates.testFailureRate > (thresholds.testFailureRatePercent || 5) * 0.8;
256
+ const buildWarning = rates.buildFailureRate > (thresholds.buildFailureRatePercent || 2) * 0.8;
257
+ const deployWarning = rates.deployRollbackRate > (thresholds.deployRollbackRatePercent || 10) * 0.8;
258
+
259
+ if (testWarning || buildWarning || deployWarning) {
260
+ status = 'WARNING';
261
+ }
262
+ }
263
+
264
+ return {
265
+ status,
266
+ rates,
267
+ thresholds: {
268
+ testFailureRatePercent: thresholds.testFailureRatePercent || 5,
269
+ buildFailureRatePercent: thresholds.buildFailureRatePercent || 2,
270
+ deployRollbackRatePercent: thresholds.deployRollbackRatePercent || 10,
271
+ },
272
+ violations,
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Resets metrics for a new tracking period.
278
+ *
279
+ * @param {string} projectRoot - Root directory of the project
280
+ * @returns {void}
281
+ */
282
+ function resetMetrics(projectRoot) {
283
+ writeMetrics(projectRoot, createEmptyMetrics());
284
+ }
285
+
286
+ module.exports = {
287
+ loadConfig,
288
+ loadMetrics,
289
+ recordTestResult,
290
+ recordBuildResult,
291
+ recordDeployResult,
292
+ getBudgetReport,
293
+ resetMetrics,
294
+ };
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Antigravity AI Kit — Hook Trigger System
3
+ *
4
+ * Event-driven lifecycle hook execution based on hooks.json.
5
+ * Evaluates hook actions and reports results with severity awareness.
6
+ *
7
+ * @module lib/hook-system
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 HOOKS_DIR = 'hooks';
19
+ const HOOKS_FILE = 'hooks.json';
20
+
21
+ /**
22
+ * @typedef {object} ActionResult
23
+ * @property {string} action - Action description
24
+ * @property {'critical' | 'high' | 'medium' | 'low'} severity - Action severity
25
+ * @property {'block' | 'warn' | 'log'} onFailure - Failure behavior
26
+ * @property {'pass' | 'fail' | 'skip'} status - Evaluation result
27
+ * @property {string} reason - Reason for status
28
+ */
29
+
30
+ /**
31
+ * @typedef {object} HookEvaluation
32
+ * @property {string} event - Hook event name
33
+ * @property {boolean} blocked - Whether any blocking action failed
34
+ * @property {number} passed - Count of passed actions
35
+ * @property {number} failed - Count of failed actions
36
+ * @property {number} skipped - Count of skipped actions
37
+ * @property {ActionResult[]} results - Individual action results
38
+ */
39
+
40
+ /**
41
+ * Loads hooks definition from hooks.json.
42
+ *
43
+ * @param {string} projectRoot - Root directory of the project
44
+ * @returns {object} Parsed hooks config
45
+ */
46
+ function loadHooks(projectRoot) {
47
+ const hooksPath = path.join(projectRoot, AGENT_DIR, HOOKS_DIR, HOOKS_FILE);
48
+
49
+ if (!fs.existsSync(hooksPath)) {
50
+ return { hooks: [] };
51
+ }
52
+
53
+ return JSON.parse(fs.readFileSync(hooksPath, 'utf-8'));
54
+ }
55
+
56
+ /**
57
+ * Gets the action definitions for a specific event.
58
+ *
59
+ * @param {string} eventName - Hook event name (e.g., 'session-start')
60
+ * @param {string} projectRoot - Root directory of the project
61
+ * @returns {object[]} Action definitions for this event
62
+ */
63
+ function getHookActions(eventName, projectRoot) {
64
+ const config = loadHooks(projectRoot);
65
+ const hook = (config.hooks || []).find((h) => h.event === eventName);
66
+
67
+ if (!hook) {
68
+ return [];
69
+ }
70
+
71
+ return hook.actions || [];
72
+ }
73
+
74
+ /**
75
+ * Evaluates a hook event against the current project state.
76
+ *
77
+ * This performs a "check" pass — it determines which actions would
78
+ * pass or fail without actually executing them. Actions are evaluated
79
+ * based on the existence of required files and configurations.
80
+ *
81
+ * @param {string} eventName - Hook event name
82
+ * @param {object} context - Evaluation context
83
+ * @param {string} context.projectRoot - Root directory of the project
84
+ * @param {boolean} [context.gitClean] - Whether git status is clean
85
+ * @param {boolean} [context.testsPass] - Whether tests pass
86
+ * @param {boolean} [context.buildPass] - Whether build passes
87
+ * @param {boolean} [context.lintPass] - Whether lint passes
88
+ * @returns {HookEvaluation}
89
+ */
90
+ function evaluateHook(eventName, context) {
91
+ const projectRoot = context.projectRoot;
92
+ const actions = getHookActions(eventName, projectRoot);
93
+
94
+ if (actions.length === 0) {
95
+ return { event: eventName, blocked: false, passed: 0, failed: 0, skipped: 0, results: [] };
96
+ }
97
+
98
+ /** @type {ActionResult[]} */
99
+ const results = [];
100
+ let blocked = false;
101
+
102
+ for (const action of actions) {
103
+ const result = evaluateAction(action, context);
104
+ results.push(result);
105
+
106
+ if (result.status === 'fail' && action.onFailure === 'block') {
107
+ blocked = true;
108
+ }
109
+ }
110
+
111
+ return {
112
+ event: eventName,
113
+ blocked,
114
+ passed: results.filter((r) => r.status === 'pass').length,
115
+ failed: results.filter((r) => r.status === 'fail').length,
116
+ skipped: results.filter((r) => r.status === 'skip').length,
117
+ results,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Evaluates a single action based on context.
123
+ *
124
+ * @param {object} action - Action definition from hooks.json
125
+ * @param {object} context - Evaluation context
126
+ * @returns {ActionResult}
127
+ */
128
+ function evaluateAction(action, context) {
129
+ const actionLower = action.action.toLowerCase();
130
+ const projectRoot = context.projectRoot;
131
+
132
+ // File existence checks
133
+ if (actionLower.includes('session-context.md') || actionLower.includes('session-state.json')) {
134
+ const targetFile = actionLower.includes('session-context.md') ? 'session-context.md' : 'session-state.json';
135
+ const filePath = path.join(projectRoot, AGENT_DIR, targetFile);
136
+ const exists = fs.existsSync(filePath);
137
+
138
+ return {
139
+ action: action.action,
140
+ severity: action.severity,
141
+ onFailure: action.onFailure,
142
+ status: exists ? 'pass' : 'fail',
143
+ reason: exists ? `${targetFile} exists` : `${targetFile} not found`,
144
+ };
145
+ }
146
+
147
+ if (actionLower.includes('loading-rules.json') || actionLower.includes('workflow-state.json')) {
148
+ const targetFile = actionLower.includes('loading-rules.json') ? 'loading-rules.json' : 'workflow-state.json';
149
+ const filePath = path.join(projectRoot, AGENT_DIR, 'engine', targetFile);
150
+ const exists = fs.existsSync(filePath);
151
+
152
+ return {
153
+ action: action.action,
154
+ severity: action.severity,
155
+ onFailure: action.onFailure,
156
+ status: exists ? 'pass' : 'fail',
157
+ reason: exists ? `${targetFile} exists and readable` : `${targetFile} not found`,
158
+ };
159
+ }
160
+
161
+ // Git status check
162
+ if (actionLower.includes('git status')) {
163
+ const gitClean = context.gitClean !== undefined ? context.gitClean : true;
164
+ return {
165
+ action: action.action,
166
+ severity: action.severity,
167
+ onFailure: action.onFailure,
168
+ status: gitClean ? 'pass' : 'fail',
169
+ reason: gitClean ? 'Git status clean' : 'Git has uncommitted changes',
170
+ };
171
+ }
172
+
173
+ // Build/test/lint checks
174
+ if (actionLower.includes('build passes') || actionLower.includes('npm run build')) {
175
+ return {
176
+ action: action.action,
177
+ severity: action.severity,
178
+ onFailure: action.onFailure,
179
+ status: context.buildPass ? 'pass' : context.buildPass === false ? 'fail' : 'skip',
180
+ reason: context.buildPass ? 'Build passes' : context.buildPass === false ? 'Build failed' : 'Build not checked',
181
+ };
182
+ }
183
+
184
+ if (actionLower.includes('tests pass') || actionLower.includes('npm test')) {
185
+ return {
186
+ action: action.action,
187
+ severity: action.severity,
188
+ onFailure: action.onFailure,
189
+ status: context.testsPass ? 'pass' : context.testsPass === false ? 'fail' : 'skip',
190
+ reason: context.testsPass ? 'Tests pass' : context.testsPass === false ? 'Tests failed' : 'Tests not checked',
191
+ };
192
+ }
193
+
194
+ if (actionLower.includes('lint passes') || actionLower.includes('npm run lint')) {
195
+ return {
196
+ action: action.action,
197
+ severity: action.severity,
198
+ onFailure: action.onFailure,
199
+ status: context.lintPass ? 'pass' : context.lintPass === false ? 'fail' : 'skip',
200
+ reason: context.lintPass ? 'Lint passes' : context.lintPass === false ? 'Lint failed' : 'Lint not checked',
201
+ };
202
+ }
203
+
204
+ // Default: skip actions we can't evaluate programmatically
205
+ return {
206
+ action: action.action,
207
+ severity: action.severity,
208
+ onFailure: action.onFailure,
209
+ status: 'skip',
210
+ reason: 'Cannot evaluate programmatically — requires agent execution',
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Lists all available hook events.
216
+ *
217
+ * @param {string} projectRoot - Root directory of the project
218
+ * @returns {Array<{ event: string, description: string, actionCount: number }>}
219
+ */
220
+ function listEvents(projectRoot) {
221
+ const config = loadHooks(projectRoot);
222
+
223
+ return (config.hooks || []).map((hook) => ({
224
+ event: hook.event,
225
+ description: hook.description,
226
+ actionCount: (hook.actions || []).length,
227
+ }));
228
+ }
229
+
230
+ /**
231
+ * Generates a full hook readiness report for all events.
232
+ *
233
+ * @param {string} projectRoot - Root directory of the project
234
+ * @returns {{ events: HookEvaluation[], totalActions: number, readyCount: number }}
235
+ */
236
+ function getHookReport(projectRoot) {
237
+ const events = listEvents(projectRoot);
238
+ const context = { projectRoot };
239
+
240
+ const evaluations = events.map((e) => evaluateHook(e.event, context));
241
+ const totalActions = evaluations.reduce((sum, e) => sum + e.passed + e.failed + e.skipped, 0);
242
+ const readyCount = evaluations.filter((e) => !e.blocked).length;
243
+
244
+ return { events: evaluations, totalActions, readyCount };
245
+ }
246
+
247
+ module.exports = {
248
+ getHookActions,
249
+ evaluateHook,
250
+ listEvents,
251
+ getHookReport,
252
+ };