@itz4blitz/agentful 1.2.0 → 1.3.0
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/README.md +28 -1
- package/bin/cli.js +11 -1055
- package/bin/hooks/block-file-creation.js +271 -0
- package/bin/hooks/product-spec-watcher.js +151 -0
- package/lib/index.js +0 -11
- package/lib/init.js +2 -21
- package/lib/parallel-execution.js +235 -0
- package/lib/presets.js +26 -4
- package/package.json +4 -7
- package/template/.claude/agents/architect.md +2 -2
- package/template/.claude/agents/backend.md +17 -30
- package/template/.claude/agents/frontend.md +17 -39
- package/template/.claude/agents/orchestrator.md +63 -4
- package/template/.claude/agents/product-analyzer.md +1 -1
- package/template/.claude/agents/tester.md +16 -29
- package/template/.claude/commands/agentful-generate.md +221 -14
- package/template/.claude/commands/agentful-init.md +621 -0
- package/template/.claude/commands/agentful-product.md +1 -1
- package/template/.claude/commands/agentful-start.md +99 -1
- package/template/.claude/product/EXAMPLES.md +2 -2
- package/template/.claude/product/index.md +1 -1
- package/template/.claude/settings.json +22 -0
- package/template/.claude/skills/research/SKILL.md +432 -0
- package/template/CLAUDE.md +5 -6
- package/template/bin/hooks/architect-drift-detector.js +242 -0
- package/template/bin/hooks/product-spec-watcher.js +151 -0
- package/version.json +1 -1
- package/bin/hooks/post-agent.js +0 -101
- package/bin/hooks/post-feature.js +0 -227
- package/bin/hooks/pre-agent.js +0 -118
- package/bin/hooks/pre-feature.js +0 -138
- package/lib/VALIDATION_README.md +0 -455
- package/lib/ci/claude-action-integration.js +0 -641
- package/lib/ci/index.js +0 -10
- package/lib/core/analyzer.js +0 -497
- package/lib/core/cli.js +0 -141
- package/lib/core/detectors/conventions.js +0 -342
- package/lib/core/detectors/framework.js +0 -276
- package/lib/core/detectors/index.js +0 -15
- package/lib/core/detectors/language.js +0 -199
- package/lib/core/detectors/patterns.js +0 -356
- package/lib/core/generator.js +0 -626
- package/lib/core/index.js +0 -9
- package/lib/core/output-parser.js +0 -458
- package/lib/core/storage.js +0 -515
- package/lib/core/templates.js +0 -556
- package/lib/pipeline/cli.js +0 -423
- package/lib/pipeline/engine.js +0 -928
- package/lib/pipeline/executor.js +0 -440
- package/lib/pipeline/index.js +0 -33
- package/lib/pipeline/integrations.js +0 -559
- package/lib/pipeline/schemas.js +0 -288
- package/lib/remote/client.js +0 -361
- package/lib/server/auth.js +0 -270
- package/lib/server/client-example.js +0 -190
- package/lib/server/executor.js +0 -477
- package/lib/server/index.js +0 -494
- package/lib/update-helpers.js +0 -505
- package/lib/validation.js +0 -460
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Architect Drift Detector Hook
|
|
5
|
+
*
|
|
6
|
+
* Detects when project changes require architect to re-analyze and update skills/agents.
|
|
7
|
+
*
|
|
8
|
+
* Triggers re-analysis when:
|
|
9
|
+
* - New dependencies added (package.json, requirements.txt, go.mod, etc.)
|
|
10
|
+
* - Tech stack changes (switched frameworks)
|
|
11
|
+
* - New patterns emerge (5+ files violating existing conventions)
|
|
12
|
+
* - Skills are stale (last analysis > 7 days old)
|
|
13
|
+
*
|
|
14
|
+
* Run: After any Write/Edit operation by agents
|
|
15
|
+
* Action: Updates .agentful/architecture.json with needs_reanalysis: true
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import crypto from 'crypto';
|
|
21
|
+
|
|
22
|
+
const ARCHITECTURE_FILE = '.agentful/architecture.json';
|
|
23
|
+
const STALE_THRESHOLD_DAYS = 7;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Detect if architect needs to re-run
|
|
27
|
+
*/
|
|
28
|
+
function detectArchitectDrift() {
|
|
29
|
+
try {
|
|
30
|
+
// Load current architecture analysis
|
|
31
|
+
const arch = loadArchitecture();
|
|
32
|
+
|
|
33
|
+
// Check various drift indicators
|
|
34
|
+
const driftReasons = [];
|
|
35
|
+
|
|
36
|
+
// 1. Check if dependency files changed
|
|
37
|
+
if (dependenciesChanged(arch)) {
|
|
38
|
+
driftReasons.push('dependencies_changed');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Check if tech stack files modified
|
|
42
|
+
if (techStackFilesModified(arch)) {
|
|
43
|
+
driftReasons.push('tech_stack_modified');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. Check if analysis is stale
|
|
47
|
+
if (analysisIsStale(arch)) {
|
|
48
|
+
driftReasons.push('analysis_stale');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 4. Check if new patterns emerged (heuristic: lots of new files)
|
|
52
|
+
if (newPatternsDetected(arch)) {
|
|
53
|
+
driftReasons.push('new_patterns_detected');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If drift detected, mark for re-analysis
|
|
57
|
+
if (driftReasons.length > 0) {
|
|
58
|
+
markForReanalysis(arch, driftReasons);
|
|
59
|
+
console.log(`⚠️ Architect drift detected: ${driftReasons.join(', ')}`);
|
|
60
|
+
console.log(' Run /agentful-generate to update skills and agents');
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return false;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// Fail silently - don't block operations if architecture.json missing
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Load architecture.json
|
|
73
|
+
*/
|
|
74
|
+
function loadArchitecture() {
|
|
75
|
+
if (!fs.existsSync(ARCHITECTURE_FILE)) {
|
|
76
|
+
// First run - architect hasn't analyzed yet
|
|
77
|
+
return {
|
|
78
|
+
last_analysis: null,
|
|
79
|
+
dependency_hashes: {},
|
|
80
|
+
file_count: 0,
|
|
81
|
+
needs_reanalysis: true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return JSON.parse(fs.readFileSync(ARCHITECTURE_FILE, 'utf8'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if dependency files changed
|
|
90
|
+
*/
|
|
91
|
+
function dependenciesChanged(arch) {
|
|
92
|
+
const depFiles = [
|
|
93
|
+
'package.json',
|
|
94
|
+
'package-lock.json',
|
|
95
|
+
'requirements.txt',
|
|
96
|
+
'pyproject.toml',
|
|
97
|
+
'Pipfile',
|
|
98
|
+
'go.mod',
|
|
99
|
+
'go.sum',
|
|
100
|
+
'Gemfile',
|
|
101
|
+
'Gemfile.lock',
|
|
102
|
+
'composer.json',
|
|
103
|
+
'pom.xml',
|
|
104
|
+
'build.gradle',
|
|
105
|
+
'Cargo.toml'
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
for (const file of depFiles) {
|
|
109
|
+
if (!fs.existsSync(file)) continue;
|
|
110
|
+
|
|
111
|
+
const currentHash = hashFile(file);
|
|
112
|
+
const previousHash = arch.dependency_hashes?.[file];
|
|
113
|
+
|
|
114
|
+
if (previousHash && currentHash !== previousHash) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if tech stack config files modified
|
|
124
|
+
*/
|
|
125
|
+
function techStackFilesModified(arch) {
|
|
126
|
+
const configFiles = [
|
|
127
|
+
'tsconfig.json',
|
|
128
|
+
'next.config.js',
|
|
129
|
+
'vite.config.js',
|
|
130
|
+
'webpack.config.js',
|
|
131
|
+
'django/settings.py',
|
|
132
|
+
'config/application.rb',
|
|
133
|
+
'nest-cli.json'
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
for (const file of configFiles) {
|
|
137
|
+
if (!fs.existsSync(file)) continue;
|
|
138
|
+
|
|
139
|
+
const currentHash = hashFile(file);
|
|
140
|
+
const previousHash = arch.dependency_hashes?.[file];
|
|
141
|
+
|
|
142
|
+
if (previousHash && currentHash !== previousHash) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check if analysis is stale (>7 days old)
|
|
152
|
+
*/
|
|
153
|
+
function analysisIsStale(arch) {
|
|
154
|
+
if (!arch.last_analysis) return true;
|
|
155
|
+
|
|
156
|
+
const lastAnalysis = new Date(arch.last_analysis);
|
|
157
|
+
const daysSinceAnalysis = (Date.now() - lastAnalysis.getTime()) / (1000 * 60 * 60 * 24);
|
|
158
|
+
|
|
159
|
+
return daysSinceAnalysis > STALE_THRESHOLD_DAYS;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Heuristic: Check if significant new code added (potential new patterns)
|
|
164
|
+
*/
|
|
165
|
+
function newPatternsDetected(arch) {
|
|
166
|
+
// Count current files in src/
|
|
167
|
+
const currentFileCount = countSourceFiles();
|
|
168
|
+
const previousFileCount = arch.file_count || 0;
|
|
169
|
+
|
|
170
|
+
// If 20% more files added, might have new patterns
|
|
171
|
+
const growthRatio = (currentFileCount - previousFileCount) / Math.max(previousFileCount, 1);
|
|
172
|
+
|
|
173
|
+
return growthRatio > 0.2;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Count source files
|
|
178
|
+
*/
|
|
179
|
+
function countSourceFiles() {
|
|
180
|
+
const srcDirs = ['src', 'app', 'lib', 'pages', 'components'];
|
|
181
|
+
let count = 0;
|
|
182
|
+
|
|
183
|
+
for (const dir of srcDirs) {
|
|
184
|
+
if (fs.existsSync(dir)) {
|
|
185
|
+
count += countFilesRecursive(dir);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return count;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Count files recursively
|
|
194
|
+
*/
|
|
195
|
+
function countFilesRecursive(dir) {
|
|
196
|
+
let count = 0;
|
|
197
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
198
|
+
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
if (entry.name.startsWith('.')) continue;
|
|
201
|
+
if (entry.name === 'node_modules') continue;
|
|
202
|
+
|
|
203
|
+
const fullPath = path.join(dir, entry.name);
|
|
204
|
+
|
|
205
|
+
if (entry.isDirectory()) {
|
|
206
|
+
count += countFilesRecursive(fullPath);
|
|
207
|
+
} else if (entry.isFile()) {
|
|
208
|
+
count++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return count;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Hash a file
|
|
217
|
+
*/
|
|
218
|
+
function hashFile(filePath) {
|
|
219
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
220
|
+
return crypto.createHash('md5').update(content).digest('hex');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Mark architecture.json for re-analysis
|
|
225
|
+
*/
|
|
226
|
+
function markForReanalysis(arch, reasons) {
|
|
227
|
+
arch.needs_reanalysis = true;
|
|
228
|
+
arch.drift_reasons = reasons;
|
|
229
|
+
arch.drift_detected_at = new Date().toISOString();
|
|
230
|
+
|
|
231
|
+
// Ensure directory exists
|
|
232
|
+
const dir = path.dirname(ARCHITECTURE_FILE);
|
|
233
|
+
if (!fs.existsSync(dir)) {
|
|
234
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
fs.writeFileSync(ARCHITECTURE_FILE, JSON.stringify(arch, null, 2));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Run detection
|
|
241
|
+
const driftDetected = detectArchitectDrift();
|
|
242
|
+
process.exit(driftDetected ? 1 : 0);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Product Spec Watcher Hook
|
|
5
|
+
*
|
|
6
|
+
* Triggered on Write/Edit of .claude/product/**.md files
|
|
7
|
+
* Auto-triggers /agentful-generate when product spec is updated
|
|
8
|
+
*
|
|
9
|
+
* Use case:
|
|
10
|
+
* 1. User runs /agentful-init → creates .claude/product/index.md
|
|
11
|
+
* 2. This hook detects the file creation
|
|
12
|
+
* 3. Checks if from /agentful-init flow
|
|
13
|
+
* 4. Auto-triggers agent generation with BOTH tech stack + requirements
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
// Get tool use metadata from environment
|
|
20
|
+
const toolUseEnv = process.env.CLAUDE_TOOL_USE || '{}';
|
|
21
|
+
let toolUse;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
toolUse = JSON.parse(toolUseEnv);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Invalid JSON - exit silently
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Get the file path that was written/edited
|
|
31
|
+
const filePath = toolUse.parameters?.file_path || '';
|
|
32
|
+
|
|
33
|
+
// Only trigger on product spec files
|
|
34
|
+
if (!filePath.includes('.claude/product/')) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get the root directory
|
|
39
|
+
const getRepoRoot = () => {
|
|
40
|
+
try {
|
|
41
|
+
const { execSync } = require('child_process');
|
|
42
|
+
return execSync('git rev-parse --show-toplevel', {
|
|
43
|
+
encoding: 'utf8'
|
|
44
|
+
}).trim();
|
|
45
|
+
} catch {
|
|
46
|
+
// Not a git repo - use cwd
|
|
47
|
+
return process.cwd();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const repoRoot = getRepoRoot();
|
|
52
|
+
const setupProgressPath = path.join(repoRoot, '.agentful', 'setup-progress.json');
|
|
53
|
+
const architecturePath = path.join(repoRoot, '.agentful', 'architecture.json');
|
|
54
|
+
|
|
55
|
+
// Check if this is from /agentful-init flow
|
|
56
|
+
let isFromInit = false;
|
|
57
|
+
let setupProgress = null;
|
|
58
|
+
|
|
59
|
+
if (fs.existsSync(setupProgressPath)) {
|
|
60
|
+
try {
|
|
61
|
+
setupProgress = JSON.parse(fs.readFileSync(setupProgressPath, 'utf8'));
|
|
62
|
+
|
|
63
|
+
// Check if setup just completed but agents not yet generated
|
|
64
|
+
isFromInit = setupProgress.completed &&
|
|
65
|
+
!setupProgress.agents_generated &&
|
|
66
|
+
// Must be recent (within last 5 minutes)
|
|
67
|
+
(Date.now() - new Date(setupProgress.timestamp).getTime()) < 5 * 60 * 1000;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// Ignore JSON parse errors
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if agents already exist
|
|
74
|
+
const hasExistingAgents = fs.existsSync(architecturePath);
|
|
75
|
+
|
|
76
|
+
if (isFromInit && !hasExistingAgents) {
|
|
77
|
+
console.log(`
|
|
78
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
79
|
+
Product Specification Updated
|
|
80
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
81
|
+
|
|
82
|
+
File: ${path.relative(repoRoot, filePath)}
|
|
83
|
+
|
|
84
|
+
Auto-triggering agent generation with:
|
|
85
|
+
✓ Tech stack (detected)
|
|
86
|
+
✓ Product requirements (from /agentful-init)
|
|
87
|
+
|
|
88
|
+
This ensures specialized agents are tailored to your product.
|
|
89
|
+
|
|
90
|
+
Starting analysis...
|
|
91
|
+
`);
|
|
92
|
+
|
|
93
|
+
// Mark that we're about to generate agents
|
|
94
|
+
if (setupProgress) {
|
|
95
|
+
setupProgress.agents_generation_triggered = true;
|
|
96
|
+
setupProgress.agents_generation_timestamp = new Date().toISOString();
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
fs.writeFileSync(setupProgressPath, JSON.stringify(setupProgress, null, 2));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
// Non-fatal - continue anyway
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// NOTE: The actual triggering of /agentful-generate would happen here
|
|
106
|
+
// This depends on Claude Code's slash command triggering system
|
|
107
|
+
// For now, we just notify the user
|
|
108
|
+
|
|
109
|
+
console.log(`
|
|
110
|
+
⏭️ Next: Agent generation will continue automatically.
|
|
111
|
+
|
|
112
|
+
If it doesn't start automatically, run:
|
|
113
|
+
/agentful-generate
|
|
114
|
+
`);
|
|
115
|
+
|
|
116
|
+
} else if (!isFromInit && !hasExistingAgents) {
|
|
117
|
+
// Manual edit of product spec, but no agents yet
|
|
118
|
+
console.log(`
|
|
119
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
120
|
+
Product Specification Updated
|
|
121
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
122
|
+
|
|
123
|
+
File: ${path.relative(repoRoot, filePath)}
|
|
124
|
+
|
|
125
|
+
To generate specialized agents based on your product spec:
|
|
126
|
+
/agentful-generate
|
|
127
|
+
|
|
128
|
+
Or to use the guided setup:
|
|
129
|
+
/agentful-init
|
|
130
|
+
`);
|
|
131
|
+
|
|
132
|
+
} else if (hasExistingAgents) {
|
|
133
|
+
// Agents already exist - notify about regeneration
|
|
134
|
+
console.log(`
|
|
135
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
136
|
+
Product Specification Updated
|
|
137
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
138
|
+
|
|
139
|
+
File: ${path.relative(repoRoot, filePath)}
|
|
140
|
+
|
|
141
|
+
Your product spec changed, but agents already exist.
|
|
142
|
+
|
|
143
|
+
To regenerate agents with updated requirements:
|
|
144
|
+
/agentful-generate
|
|
145
|
+
|
|
146
|
+
Or continue with existing agents:
|
|
147
|
+
/agentful-start
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
process.exit(0);
|
package/version.json
CHANGED
package/bin/hooks/post-agent.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// post-agent.js
|
|
3
|
-
// Tracks agent execution metrics
|
|
4
|
-
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
|
|
7
|
-
const METRICS_FILE = '.agentful/agent-metrics.json';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Track agent execution metrics
|
|
11
|
-
* @param {Object} options - Tracking options
|
|
12
|
-
* @param {string} options.agentName - Name of the agent
|
|
13
|
-
* @param {string} [options.feature] - Feature being worked on
|
|
14
|
-
* @param {string} [options.domain] - Domain context
|
|
15
|
-
* @param {string} [options.timestamp] - ISO timestamp (defaults to current time)
|
|
16
|
-
* @returns {Object} - Result with success flag and optional error
|
|
17
|
-
*/
|
|
18
|
-
export function trackAgentMetrics({ agentName, feature = '', domain = '', timestamp = new Date().toISOString() }) {
|
|
19
|
-
// Exit successfully if no agent specified
|
|
20
|
-
if (!agentName) {
|
|
21
|
-
return { success: true };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Ensure .agentful directory exists
|
|
25
|
-
const metricsDir = METRICS_FILE.substring(0, METRICS_FILE.lastIndexOf('/'));
|
|
26
|
-
if (!fs.existsSync(metricsDir)) {
|
|
27
|
-
fs.mkdirSync(metricsDir, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Create metrics file if it doesn't exist
|
|
31
|
-
if (!fs.existsSync(METRICS_FILE)) {
|
|
32
|
-
const initialMetrics = {
|
|
33
|
-
invocations: {},
|
|
34
|
-
last_invocation: null,
|
|
35
|
-
feature_hooks: []
|
|
36
|
-
};
|
|
37
|
-
fs.writeFileSync(METRICS_FILE, JSON.stringify(initialMetrics, null, 2));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate and read existing metrics file
|
|
41
|
-
let metrics;
|
|
42
|
-
if (!fs.existsSync(METRICS_FILE)) {
|
|
43
|
-
// File doesn't exist, create new structure
|
|
44
|
-
metrics = {
|
|
45
|
-
invocations: {},
|
|
46
|
-
last_invocation: null,
|
|
47
|
-
feature_hooks: []
|
|
48
|
-
};
|
|
49
|
-
} else {
|
|
50
|
-
try {
|
|
51
|
-
const metricsContent = fs.readFileSync(METRICS_FILE, 'utf8');
|
|
52
|
-
metrics = JSON.parse(metricsContent);
|
|
53
|
-
} catch (err) {
|
|
54
|
-
console.log('WARNING: agent-metrics.json is corrupted, recreating');
|
|
55
|
-
metrics = {
|
|
56
|
-
invocations: {},
|
|
57
|
-
last_invocation: null,
|
|
58
|
-
feature_hooks: []
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Update invocation count for this agent
|
|
64
|
-
const currentCount = metrics.invocations[agentName] || 0;
|
|
65
|
-
const newCount = currentCount + 1;
|
|
66
|
-
|
|
67
|
-
// Update metrics
|
|
68
|
-
metrics.invocations[agentName] = newCount;
|
|
69
|
-
metrics.last_invocation = {
|
|
70
|
-
agent: agentName,
|
|
71
|
-
timestamp: timestamp,
|
|
72
|
-
feature: feature,
|
|
73
|
-
domain: domain
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// Write updated metrics
|
|
77
|
-
try {
|
|
78
|
-
fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
|
|
79
|
-
return { success: true };
|
|
80
|
-
} catch (err) {
|
|
81
|
-
console.error('WARNING: Failed to write agent-metrics.json');
|
|
82
|
-
return { success: true, error: err.message };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// CLI entrypoint
|
|
87
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
88
|
-
const AGENT_NAME = process.env.AGENTFUL_AGENT || '';
|
|
89
|
-
const FEATURE = process.env.AGENTFUL_FEATURE || '';
|
|
90
|
-
const DOMAIN = process.env.AGENTFUL_DOMAIN || '';
|
|
91
|
-
const TIMESTAMP = new Date().toISOString();
|
|
92
|
-
|
|
93
|
-
trackAgentMetrics({
|
|
94
|
-
agentName: AGENT_NAME,
|
|
95
|
-
feature: FEATURE,
|
|
96
|
-
domain: DOMAIN,
|
|
97
|
-
timestamp: TIMESTAMP
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
process.exit(0);
|
|
101
|
-
}
|