agentsys 5.4.1 → 5.6.4
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/.claude-plugin/marketplace.json +36 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +11 -0
- package/README.md +187 -17
- package/lib/adapter-transforms.js +1 -366
- package/lib/binary/index.js +398 -0
- package/lib/binary/version.js +16 -0
- package/lib/collectors/git.js +139 -0
- package/lib/collectors/index.js +10 -1
- package/lib/cross-platform/index.js +3 -13
- package/lib/discovery/index.js +1 -48
- package/lib/index.js +2 -0
- package/lib/patterns/cli-enhancers.js +2 -11
- package/lib/platform/state-dir.js +2 -16
- package/package.json +1 -1
- package/scripts/gen-adapters.js +4 -2
- package/scripts/generate-docs.js +122 -31
- package/scripts/validate-counts.js +1 -1
- package/scripts/validate-cross-platform-docs.js +5 -2
- package/site/content.json +76 -51
package/lib/discovery/index.js
CHANGED
|
@@ -163,14 +163,11 @@ function discoverCommands(repoRoot) {
|
|
|
163
163
|
|
|
164
164
|
const files = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md')).sort();
|
|
165
165
|
for (const file of files) {
|
|
166
|
-
const baseName = file.replace(/\.md$/, '');
|
|
167
|
-
// Validate filename contains only safe characters (no path traversal)
|
|
168
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(baseName)) continue;
|
|
169
166
|
const filePath = path.join(commandsDir, file);
|
|
170
167
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
171
168
|
const frontmatter = parseFrontmatter(content);
|
|
172
169
|
commands.push({
|
|
173
|
-
name:
|
|
170
|
+
name: file.replace(/\.md$/, ''),
|
|
174
171
|
plugin,
|
|
175
172
|
file,
|
|
176
173
|
frontmatter
|
|
@@ -340,48 +337,6 @@ function invalidateCache() {
|
|
|
340
337
|
_cacheRoot = null;
|
|
341
338
|
}
|
|
342
339
|
|
|
343
|
-
/**
|
|
344
|
-
* Build Cursor rule mappings from discovered commands.
|
|
345
|
-
* Returns [ruleName, pluginName, sourceFile, description, type, globs] tuples.
|
|
346
|
-
* Uses cursor-description frontmatter field, falls back to codex-description, then description.
|
|
347
|
-
*
|
|
348
|
-
* @param {string} [repoRoot] - Repository root path
|
|
349
|
-
* @returns {Array<[string, string, string, string, string, string]>}
|
|
350
|
-
*/
|
|
351
|
-
function getCursorRuleMappings(repoRoot) {
|
|
352
|
-
const commands = discoverCommands(repoRoot);
|
|
353
|
-
return commands.map(cmd => {
|
|
354
|
-
const description = cmd.frontmatter['cursor-description'] ||
|
|
355
|
-
cmd.frontmatter['codex-description'] ||
|
|
356
|
-
cmd.frontmatter.description ||
|
|
357
|
-
'';
|
|
358
|
-
const type = cmd.frontmatter.type || 'command';
|
|
359
|
-
const globs = cmd.frontmatter.globs || '';
|
|
360
|
-
return [`agentsys-${cmd.plugin}-${cmd.name}`, cmd.plugin, cmd.file, description, type, globs];
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Build Kiro steering mappings from discovered commands.
|
|
366
|
-
* Returns [steeringName, pluginName, sourceFile, description] tuples.
|
|
367
|
-
* Uses kiro-description frontmatter field, falls back to cursor-description,
|
|
368
|
-
* codex-description, then description.
|
|
369
|
-
*
|
|
370
|
-
* @param {string} [repoRoot] - Repository root path
|
|
371
|
-
* @returns {Array<[string, string, string, string]>}
|
|
372
|
-
*/
|
|
373
|
-
function getKiroSteeringMappings(repoRoot) {
|
|
374
|
-
const commands = discoverCommands(repoRoot);
|
|
375
|
-
return commands.map(cmd => {
|
|
376
|
-
const description = cmd.frontmatter['kiro-description'] ||
|
|
377
|
-
cmd.frontmatter['cursor-description'] ||
|
|
378
|
-
cmd.frontmatter['codex-description'] ||
|
|
379
|
-
cmd.frontmatter.description ||
|
|
380
|
-
'';
|
|
381
|
-
return [cmd.name, cmd.plugin, cmd.file, description];
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
340
|
module.exports = {
|
|
386
341
|
parseFrontmatter,
|
|
387
342
|
isValidPluginName,
|
|
@@ -392,8 +347,6 @@ module.exports = {
|
|
|
392
347
|
discoverAll,
|
|
393
348
|
getCommandMappings,
|
|
394
349
|
getCodexSkillMappings,
|
|
395
|
-
getCursorRuleMappings,
|
|
396
|
-
getKiroSteeringMappings,
|
|
397
350
|
getPluginPrefixRegex,
|
|
398
351
|
invalidateCache
|
|
399
352
|
};
|
package/lib/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const repoMap = require('./repo-map');
|
|
|
29
29
|
const perf = require('./perf');
|
|
30
30
|
const collectors = require('./collectors');
|
|
31
31
|
const discoveryModule = require('./discovery');
|
|
32
|
+
const binary = require('./binary');
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Platform detection and verification utilities
|
|
@@ -253,6 +254,7 @@ module.exports = {
|
|
|
253
254
|
perf,
|
|
254
255
|
collectors,
|
|
255
256
|
discovery,
|
|
257
|
+
binary,
|
|
256
258
|
|
|
257
259
|
// Direct module access for backward compatibility
|
|
258
260
|
detectPlatform,
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const { execSync, execFileSync } = require('child_process');
|
|
16
|
-
const os = require('os');
|
|
17
16
|
const path = require('path');
|
|
18
17
|
const fs = require('fs');
|
|
19
18
|
// Note: escapeDoubleQuotes no longer needed - using execFileSync with arg arrays
|
|
@@ -346,20 +345,16 @@ function runDuplicateDetection(repoPath, options = {}) {
|
|
|
346
345
|
const minLines = options.minLines || 5;
|
|
347
346
|
const minTokens = options.minTokens || 50;
|
|
348
347
|
|
|
349
|
-
// Use temp directory for jscpd output to avoid platform-specific null device issues.
|
|
350
|
-
// On Windows, NUL passed via execFileSync (no shell) is treated as a literal filename,
|
|
351
|
-
// creating a mangled path in the working directory.
|
|
352
|
-
let tempOutputDir;
|
|
353
348
|
try {
|
|
354
|
-
tempOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'jscpd-'));
|
|
355
349
|
// Run jscpd with JSON output
|
|
356
350
|
// Use execFileSync with arg array to prevent command injection (no shell interpretation)
|
|
351
|
+
const outputPath = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
357
352
|
const args = [
|
|
358
353
|
repoPath,
|
|
359
354
|
'--min-lines', String(minLines),
|
|
360
355
|
'--min-tokens', String(minTokens),
|
|
361
356
|
'--reporters', 'json',
|
|
362
|
-
'--output',
|
|
357
|
+
'--output', outputPath,
|
|
363
358
|
'--silent'
|
|
364
359
|
];
|
|
365
360
|
|
|
@@ -398,10 +393,6 @@ function runDuplicateDetection(repoPath, options = {}) {
|
|
|
398
393
|
} catch {
|
|
399
394
|
// Tool execution failed
|
|
400
395
|
return null;
|
|
401
|
-
} finally {
|
|
402
|
-
if (tempOutputDir) {
|
|
403
|
-
try { fs.rmSync(tempOutputDir, { recursive: true, force: true }); } catch {}
|
|
404
|
-
}
|
|
405
396
|
}
|
|
406
397
|
}
|
|
407
398
|
|
|
@@ -32,12 +32,10 @@ function isDirectory(targetPath) {
|
|
|
32
32
|
* 1. AI_STATE_DIR env var (user override)
|
|
33
33
|
* 2. OpenCode detection (OPENCODE_CONFIG env or .opencode/ exists)
|
|
34
34
|
* 3. Codex detection (CODEX_HOME env or .codex/ exists)
|
|
35
|
-
* 4.
|
|
36
|
-
* 5. Cursor detection (.cursor/ exists)
|
|
37
|
-
* 6. Default to .claude (Claude Code or unknown)
|
|
35
|
+
* 4. Default to .claude (Claude Code or unknown)
|
|
38
36
|
*
|
|
39
37
|
* @param {string} [basePath=process.cwd()] - Base path to check for project directories
|
|
40
|
-
* @returns {string} State directory name (e.g., '.claude', '.opencode', '.codex'
|
|
38
|
+
* @returns {string} State directory name (e.g., '.claude', '.opencode', '.codex')
|
|
41
39
|
*/
|
|
42
40
|
function getStateDir(basePath = process.cwd()) {
|
|
43
41
|
// Check user override first
|
|
@@ -85,17 +83,6 @@ function getStateDir(basePath = process.cwd()) {
|
|
|
85
83
|
// Ignore errors, continue detection
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
// Kiro detection
|
|
89
|
-
try {
|
|
90
|
-
const kiroPath = path.join(basePath, '.kiro');
|
|
91
|
-
if (isDirectory(kiroPath)) {
|
|
92
|
-
_cachedStateDirs.set(cacheKey, '.kiro');
|
|
93
|
-
return '.kiro';
|
|
94
|
-
}
|
|
95
|
-
} catch {
|
|
96
|
-
// Ignore errors, continue detection
|
|
97
|
-
}
|
|
98
|
-
|
|
99
86
|
// Default to Claude Code
|
|
100
87
|
_cachedStateDirs.set(cacheKey, '.claude');
|
|
101
88
|
return '.claude';
|
|
@@ -125,7 +112,6 @@ function getPlatformName(basePath = process.cwd()) {
|
|
|
125
112
|
switch (stateDir) {
|
|
126
113
|
case '.opencode': return 'opencode';
|
|
127
114
|
case '.codex': return 'codex';
|
|
128
|
-
case '.kiro': return 'kiro';
|
|
129
115
|
case '.claude': return 'claude';
|
|
130
116
|
default: return 'unknown';
|
|
131
117
|
}
|
package/package.json
CHANGED
package/scripts/gen-adapters.js
CHANGED
|
@@ -120,11 +120,13 @@ function computeAdapters() {
|
|
|
120
120
|
process.exit(1);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
// --- Kiro adapters ---
|
|
123
|
+
// --- Kiro adapters (disabled - Kiro adapter generation removed) ---
|
|
124
124
|
const KIRO_PLUGIN_ROOT_PLACEHOLDER = '{{PLUGIN_INSTALL_PATH}}';
|
|
125
125
|
|
|
126
126
|
// Kiro steering files (from commands)
|
|
127
|
-
const kiroSteeringMappings = discovery.getKiroSteeringMappings
|
|
127
|
+
const kiroSteeringMappings = typeof discovery.getKiroSteeringMappings === 'function'
|
|
128
|
+
? discovery.getKiroSteeringMappings(ROOT_DIR)
|
|
129
|
+
: [];
|
|
128
130
|
for (const [steeringName, plugin, sourceFile, description] of kiroSteeringMappings) {
|
|
129
131
|
const srcPath = path.join(ROOT_DIR, 'plugins', plugin, 'commands', sourceFile);
|
|
130
132
|
if (!fs.existsSync(srcPath)) continue;
|
package/scripts/generate-docs.js
CHANGED
|
@@ -53,12 +53,62 @@ const CATEGORY_MAP = {
|
|
|
53
53
|
'sync-docs': 'Cleanup',
|
|
54
54
|
'drift-detect': 'Analysis',
|
|
55
55
|
'repo-map': 'Analysis',
|
|
56
|
-
'learn': '
|
|
56
|
+
'learn': 'AI Collaboration',
|
|
57
57
|
'agnix': 'Linting',
|
|
58
|
-
'consult': '
|
|
59
|
-
'debate': '
|
|
58
|
+
'consult': 'AI Collaboration',
|
|
59
|
+
'debate': 'AI Collaboration',
|
|
60
|
+
'skillers': 'AI Collaboration',
|
|
61
|
+
'web-ctl': 'Web',
|
|
62
|
+
'ship': 'Release',
|
|
63
|
+
'git-map': 'Analysis',
|
|
64
|
+
'onboard': 'Onboarding',
|
|
65
|
+
'can-i-help': 'Onboarding',
|
|
66
|
+
'audit-project': 'Code Review'
|
|
60
67
|
};
|
|
61
68
|
|
|
69
|
+
// Static skill definitions for cross-repo plugins (not discoverable locally)
|
|
70
|
+
const STATIC_SKILLS = [
|
|
71
|
+
{ plugin: 'next-task', name: 'orchestrate-review' },
|
|
72
|
+
{ plugin: 'next-task', name: 'discover-tasks' },
|
|
73
|
+
{ plugin: 'next-task', name: 'validate-delivery' },
|
|
74
|
+
{ plugin: 'enhance', name: 'enhance-orchestrator' },
|
|
75
|
+
{ plugin: 'enhance', name: 'enhance-plugins' },
|
|
76
|
+
{ plugin: 'enhance', name: 'enhance-agent-prompts' },
|
|
77
|
+
{ plugin: 'enhance', name: 'enhance-claude-memory' },
|
|
78
|
+
{ plugin: 'enhance', name: 'enhance-docs' },
|
|
79
|
+
{ plugin: 'enhance', name: 'enhance-prompts' },
|
|
80
|
+
{ plugin: 'enhance', name: 'enhance-hooks' },
|
|
81
|
+
{ plugin: 'enhance', name: 'enhance-skills' },
|
|
82
|
+
{ plugin: 'enhance', name: 'enhance-cross-file' },
|
|
83
|
+
{ plugin: 'perf', name: 'baseline' },
|
|
84
|
+
{ plugin: 'perf', name: 'benchmark' },
|
|
85
|
+
{ plugin: 'perf', name: 'profile' },
|
|
86
|
+
{ plugin: 'perf', name: 'theory-tester' },
|
|
87
|
+
{ plugin: 'perf', name: 'theory-gatherer' },
|
|
88
|
+
{ plugin: 'perf', name: 'code-paths' },
|
|
89
|
+
{ plugin: 'perf', name: 'investigation-logger' },
|
|
90
|
+
{ plugin: 'perf', name: 'perf-analyzer' },
|
|
91
|
+
{ plugin: 'deslop', name: 'deslop' },
|
|
92
|
+
{ plugin: 'sync-docs', name: 'sync-docs' },
|
|
93
|
+
{ plugin: 'drift-detect', name: 'drift-analysis' },
|
|
94
|
+
{ plugin: 'repo-map', name: 'repo-mapping' },
|
|
95
|
+
{ plugin: 'consult', name: 'consult' },
|
|
96
|
+
{ plugin: 'debate', name: 'debate' },
|
|
97
|
+
{ plugin: 'learn', name: 'learn' },
|
|
98
|
+
{ plugin: 'web-ctl', name: 'web-auth' },
|
|
99
|
+
{ plugin: 'web-ctl', name: 'web-browse' },
|
|
100
|
+
{ plugin: 'ship', name: 'release' },
|
|
101
|
+
{ plugin: 'skillers', name: 'skillers-compact' },
|
|
102
|
+
{ plugin: 'skillers', name: 'recommend' },
|
|
103
|
+
{ plugin: 'git-map', name: 'git-mapping' },
|
|
104
|
+
{ plugin: 'onboard', name: 'onboard' },
|
|
105
|
+
{ plugin: 'can-i-help', name: 'can-i-help' },
|
|
106
|
+
{ plugin: 'audit-project', name: 'audit-project' },
|
|
107
|
+
{ plugin: 'glidemq', name: 'glide-mq' },
|
|
108
|
+
{ plugin: 'glidemq', name: 'glide-mq-migrate-bullmq' },
|
|
109
|
+
{ plugin: 'glidemq', name: 'glide-mq-migrate-bee' }
|
|
110
|
+
];
|
|
111
|
+
|
|
62
112
|
// Purpose mapping for architecture table
|
|
63
113
|
const PURPOSE_MAP = {
|
|
64
114
|
'next-task': 'Master workflow orchestration',
|
|
@@ -73,7 +123,12 @@ const PURPOSE_MAP = {
|
|
|
73
123
|
'learn': 'Topic research and learning guides',
|
|
74
124
|
'agnix': 'Agent config linting',
|
|
75
125
|
'consult': 'Cross-tool AI consultation',
|
|
76
|
-
'debate': 'Multi-perspective debate analysis'
|
|
126
|
+
'debate': 'Multi-perspective debate analysis',
|
|
127
|
+
'web-ctl': 'Browser automation for AI agents',
|
|
128
|
+
'skillers': 'Workflow pattern learning',
|
|
129
|
+
'git-map': 'Git history analysis',
|
|
130
|
+
'onboard': 'Codebase onboarding',
|
|
131
|
+
'can-i-help': 'Contributor guidance'
|
|
77
132
|
};
|
|
78
133
|
|
|
79
134
|
// ---------------------------------------------------------------------------
|
|
@@ -122,22 +177,32 @@ function generateCommandsTable(commands) {
|
|
|
122
177
|
const COMMAND_ORDER = [
|
|
123
178
|
'next-task', 'agnix', 'ship', 'deslop', 'perf',
|
|
124
179
|
'drift-detect', 'audit-project', 'enhance',
|
|
125
|
-
'repo-map', 'sync-docs', 'learn'
|
|
180
|
+
'repo-map', 'sync-docs', 'learn', 'consult',
|
|
181
|
+
'debate', 'web-ctl', 'release', 'skillers',
|
|
182
|
+
'git-map', 'onboard', 'can-i-help'
|
|
126
183
|
];
|
|
127
184
|
|
|
128
185
|
// Command descriptions for the table (short, human-written summaries)
|
|
129
186
|
const COMMAND_SUMMARIES = {
|
|
130
|
-
'next-task': 'Task
|
|
131
|
-
'agnix': '
|
|
132
|
-
'ship': '
|
|
133
|
-
'deslop': '
|
|
134
|
-
'perf': '
|
|
135
|
-
'drift-detect': '
|
|
136
|
-
'audit-project': 'Multi-agent code review
|
|
137
|
-
'enhance': '
|
|
138
|
-
'repo-map': 'AST
|
|
139
|
-
'sync-docs': '
|
|
140
|
-
'learn': 'Research
|
|
187
|
+
'next-task': 'Task workflow: discovery, implementation, PR, merge',
|
|
188
|
+
'agnix': 'Lint agent configurations (342 rules)',
|
|
189
|
+
'ship': 'PR creation, CI monitoring, merge',
|
|
190
|
+
'deslop': 'Clean AI slop patterns',
|
|
191
|
+
'perf': 'Performance investigation with baselines and profiling',
|
|
192
|
+
'drift-detect': 'Compare plan vs implementation',
|
|
193
|
+
'audit-project': 'Multi-agent iterative code review',
|
|
194
|
+
'enhance': 'Plugin, agent, and prompt analyzers',
|
|
195
|
+
'repo-map': 'AST-based repository map',
|
|
196
|
+
'sync-docs': 'Sync documentation with code changes',
|
|
197
|
+
'learn': 'Research topics, create learning guides',
|
|
198
|
+
'consult': 'Cross-tool AI consultation',
|
|
199
|
+
'debate': 'Structured debate between AI tools',
|
|
200
|
+
'web-ctl': 'Browser automation for AI agents',
|
|
201
|
+
'release': 'Versioned release with ecosystem detection',
|
|
202
|
+
'skillers': 'Workflow pattern learning and automation',
|
|
203
|
+
'git-map': 'Git history analysis: hotspots, coupling, ownership, bus factor',
|
|
204
|
+
'onboard': 'Codebase orientation for newcomers',
|
|
205
|
+
'can-i-help': 'Match contributor skills to project needs'
|
|
141
206
|
};
|
|
142
207
|
|
|
143
208
|
// Build lookup of discovered commands
|
|
@@ -146,12 +211,15 @@ function generateCommandsTable(commands) {
|
|
|
146
211
|
if (!cmdMap[cmd.name]) cmdMap[cmd.name] = cmd;
|
|
147
212
|
}
|
|
148
213
|
|
|
149
|
-
// Emit in curated order
|
|
214
|
+
// Emit in curated order. Prefer curated summaries; fall back to discovered frontmatter.
|
|
150
215
|
const emitted = new Set();
|
|
151
216
|
for (const name of COMMAND_ORDER) {
|
|
152
|
-
|
|
217
|
+
let summary = COMMAND_SUMMARIES[name] || '';
|
|
218
|
+
if (!summary && cmdMap[name]) {
|
|
219
|
+
summary = cmdMap[name].frontmatter.description || '';
|
|
220
|
+
}
|
|
221
|
+
if (!summary && !cmdMap[name]) continue;
|
|
153
222
|
emitted.add(name);
|
|
154
|
-
const summary = COMMAND_SUMMARIES[name] || cmdMap[name].frontmatter.description || '';
|
|
155
223
|
lines.push(`| [\`/${name}\`](#${name}) | ${summary} |`);
|
|
156
224
|
}
|
|
157
225
|
|
|
@@ -171,14 +239,16 @@ function generateCommandsTable(commands) {
|
|
|
171
239
|
* Generate the skills table for README.md grouped by category.
|
|
172
240
|
*/
|
|
173
241
|
function generateSkillsTable(skills) {
|
|
174
|
-
|
|
242
|
+
// Use static skills as fallback when discovery finds nothing (cross-repo plugins)
|
|
243
|
+
const effectiveSkills = skills.length > 0 ? skills : STATIC_SKILLS;
|
|
244
|
+
const totalSkills = effectiveSkills.length;
|
|
175
245
|
|
|
176
246
|
// Group skills by category
|
|
177
247
|
const groups = {};
|
|
178
|
-
for (const skill of
|
|
248
|
+
for (const skill of effectiveSkills) {
|
|
179
249
|
const category = CATEGORY_MAP[skill.plugin] || 'Other';
|
|
180
250
|
if (!groups[category]) groups[category] = [];
|
|
181
|
-
groups[category].push(`\`${skill.
|
|
251
|
+
groups[category].push(`\`${skill.name}\``);
|
|
182
252
|
}
|
|
183
253
|
|
|
184
254
|
// Sort skills within each group
|
|
@@ -188,8 +258,9 @@ function generateSkillsTable(skills) {
|
|
|
188
258
|
|
|
189
259
|
// Defined category order
|
|
190
260
|
const categoryOrder = [
|
|
191
|
-
'
|
|
192
|
-
'
|
|
261
|
+
'Workflow', 'Enhancement', 'Performance', 'Cleanup',
|
|
262
|
+
'Code Review', 'AI Collaboration', 'Onboarding',
|
|
263
|
+
'Web', 'Release', 'Analysis', 'Linting', 'Other'
|
|
193
264
|
];
|
|
194
265
|
|
|
195
266
|
const lines = [
|
|
@@ -318,26 +389,43 @@ function generateAgentCounts(agents, plugins) {
|
|
|
318
389
|
/**
|
|
319
390
|
* Update counts in site/content.json programmatically.
|
|
320
391
|
*/
|
|
392
|
+
// Static counts for cross-repo plugins not discoverable locally
|
|
393
|
+
const STATIC_PLUGIN_COUNT = 19;
|
|
394
|
+
const STATIC_AGENT_COUNT = 38;
|
|
395
|
+
|
|
321
396
|
function updateSiteContent(plugins, agents, skills) {
|
|
322
397
|
const contentPath = path.join(ROOT_DIR, 'site', 'content.json');
|
|
323
398
|
if (!fs.existsSync(contentPath)) return null;
|
|
324
399
|
|
|
325
400
|
const content = JSON.parse(fs.readFileSync(contentPath, 'utf8'));
|
|
326
|
-
|
|
401
|
+
|
|
402
|
+
// Sync meta.version from package.json
|
|
403
|
+
const pkgPath = path.join(ROOT_DIR, 'package.json');
|
|
404
|
+
if (fs.existsSync(pkgPath)) {
|
|
405
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
406
|
+
if (content.meta && pkg.version) {
|
|
407
|
+
content.meta.version = pkg.version;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Use static counts as fallback (cross-repo plugins not discoverable locally)
|
|
412
|
+
const effectivePlugins = plugins.length > 0 ? plugins.length : STATIC_PLUGIN_COUNT;
|
|
413
|
+
const effectiveAgents = agents.length > 0 ? agents.length + ROLE_BASED_AGENT_COUNT : STATIC_AGENT_COUNT;
|
|
414
|
+
const effectiveSkills = skills.length > 0 ? skills.length : STATIC_SKILLS.length;
|
|
327
415
|
|
|
328
416
|
// Update stats array
|
|
329
417
|
if (content.stats && Array.isArray(content.stats)) {
|
|
330
418
|
for (const stat of content.stats) {
|
|
331
|
-
if (stat.label === 'Plugins') stat.value = String(
|
|
332
|
-
if (stat.label === 'Agents') stat.value = String(
|
|
333
|
-
if (stat.label === 'Skills') stat.value = String(
|
|
419
|
+
if (stat.label === 'Plugins') stat.value = String(effectivePlugins);
|
|
420
|
+
if (stat.label === 'Agents') stat.value = String(effectiveAgents);
|
|
421
|
+
if (stat.label === 'Skills') stat.value = String(effectiveSkills);
|
|
334
422
|
}
|
|
335
423
|
}
|
|
336
424
|
|
|
337
425
|
// Update agents section
|
|
338
426
|
if (content.agents) {
|
|
339
|
-
content.agents.total =
|
|
340
|
-
content.agents.file_based =
|
|
427
|
+
content.agents.total = effectiveAgents;
|
|
428
|
+
content.agents.file_based = effectiveAgents - ROLE_BASED_AGENT_COUNT;
|
|
341
429
|
content.agents.role_based = ROLE_BASED_AGENT_COUNT;
|
|
342
430
|
}
|
|
343
431
|
|
|
@@ -556,5 +644,8 @@ module.exports = {
|
|
|
556
644
|
updateSiteContent,
|
|
557
645
|
CATEGORY_MAP,
|
|
558
646
|
PURPOSE_MAP,
|
|
559
|
-
ROLE_BASED_AGENT_COUNT
|
|
647
|
+
ROLE_BASED_AGENT_COUNT,
|
|
648
|
+
STATIC_SKILLS,
|
|
649
|
+
STATIC_PLUGIN_COUNT,
|
|
650
|
+
STATIC_AGENT_COUNT
|
|
560
651
|
};
|
|
@@ -241,7 +241,7 @@ function formatCountMismatch(file, metric, expected, actual) {
|
|
|
241
241
|
*/
|
|
242
242
|
function runValidation() {
|
|
243
243
|
// When plugins/ doesn't exist, counts are not meaningful — return ok
|
|
244
|
-
if (!fs.existsSync(path.join(REPO_ROOT, 'plugins'))) {
|
|
244
|
+
if (!fs.existsSync(path.join(REPO_ROOT, 'plugins')) || fs.readdirSync(path.join(REPO_ROOT, 'plugins')).filter(f => fs.statSync(path.join(REPO_ROOT, 'plugins', f)).isDirectory()).length === 0) {
|
|
245
245
|
return {
|
|
246
246
|
status: 'ok',
|
|
247
247
|
message: 'plugins/ not present (extracted to standalone repos)',
|
|
@@ -251,11 +251,14 @@ function validateFeatureParity() {
|
|
|
251
251
|
});
|
|
252
252
|
|
|
253
253
|
// Check that all platforms document all required features
|
|
254
|
+
// Features documented in general docs (README.md) count for all platforms
|
|
255
|
+
const generalFeatures = featuresByPlatform.general || new Set();
|
|
256
|
+
|
|
254
257
|
REQUIRED_FEATURES.forEach(feature => {
|
|
255
258
|
Object.entries(featuresByPlatform).forEach(([platform, features]) => {
|
|
256
|
-
if (platform === 'general') return;
|
|
259
|
+
if (platform === 'general') return;
|
|
257
260
|
|
|
258
|
-
if (!features.has(feature)) {
|
|
261
|
+
if (!features.has(feature) && !generalFeatures.has(feature)) {
|
|
259
262
|
issues.push({
|
|
260
263
|
platform,
|
|
261
264
|
feature,
|