aios-core 3.1.0 → 3.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.
@@ -454,3 +454,47 @@ utils:
454
454
  - transaction-manager
455
455
  - usage-analytics
456
456
  - usage-tracker
457
+
458
+ # ============================================
459
+ # SECTION 12: IDE Sync System (Story 6.19)
460
+ # Automatically syncs agents to IDE command files
461
+ # ============================================
462
+ ideSync:
463
+ enabled: true
464
+ source: .aios-core/development/agents
465
+
466
+ # Target IDE configurations
467
+ targets:
468
+ claude-code:
469
+ enabled: true
470
+ path: .claude/commands/AIOS/agents
471
+ format: full-markdown-yaml
472
+ cursor:
473
+ enabled: true
474
+ path: .cursor/rules/agents
475
+ format: condensed-rules
476
+ windsurf:
477
+ enabled: true
478
+ path: .windsurf/rules/agents
479
+ format: xml-tagged-markdown
480
+ trae:
481
+ enabled: true
482
+ path: .trae/rules/agents
483
+ format: project-rules
484
+ antigravity:
485
+ enabled: true
486
+ path: .antigravity/rules/agents
487
+ format: cursor-style
488
+
489
+ # Redirects for deprecated/renamed agents
490
+ redirects:
491
+ aios-developer: aios-master
492
+ aios-orchestrator: aios-master
493
+ db-sage: data-engineer
494
+ github-devops: devops
495
+
496
+ # Validation settings
497
+ validation:
498
+ strictMode: true
499
+ failOnDrift: true
500
+ failOnOrphaned: false
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Agent Parser - Extracts YAML and markdown sections from agent files
3
+ * @story 6.19 - IDE Command Auto-Sync System
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const yaml = require('js-yaml');
9
+
10
+ /**
11
+ * Extract YAML block from markdown content
12
+ * @param {string} content - Full markdown content
13
+ * @returns {string|null} - YAML content without fences, or null if not found
14
+ */
15
+ function extractYamlBlock(content) {
16
+ // Match ```yaml ... ``` block
17
+ const yamlMatch = content.match(/```yaml\s*\n([\s\S]*?)\n```/);
18
+ if (yamlMatch && yamlMatch[1]) {
19
+ return yamlMatch[1].trim();
20
+ }
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * Parse YAML content safely with fallback for problematic patterns
26
+ * @param {string} yamlContent - YAML string to parse
27
+ * @returns {object|null} - Parsed object or null on error
28
+ */
29
+ function parseYaml(yamlContent) {
30
+ try {
31
+ return yaml.load(yamlContent);
32
+ } catch (error) {
33
+ // Try to fix common YAML issues before failing
34
+ try {
35
+ // Fix command format issues with {placeholders} in keys
36
+ // Convert "- key {param}: value" to "- name: key {param}\n description: value"
37
+ let fixed = yamlContent.replace(
38
+ /^(\s*)-\s+([a-z-]+)\s*(\{[^}]+\})?:\s*(.+)$/gm,
39
+ (match, indent, name, param, desc) => {
40
+ const fullName = param ? `${name} ${param}` : name;
41
+ return `${indent}- name: "${fullName}"\n${indent} description: "${desc.replace(/"/g, '\\"')}"`;
42
+ }
43
+ );
44
+
45
+ // Fix pipe patterns with invalid YAML
46
+ fixed = fixed.replace(/:\s*"[^"]*"\s*\|\s*"[^"]*"/g, (match) => {
47
+ // Convert "value1" | "value2" to list
48
+ return `: [${match.slice(2)}]`;
49
+ });
50
+
51
+ return yaml.load(fixed);
52
+ } catch (fixError) {
53
+ console.error(`YAML parse error: ${error.message}`);
54
+ return null;
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Extract markdown section by heading
61
+ * @param {string} content - Full markdown content
62
+ * @param {string} heading - Heading to find (without #)
63
+ * @returns {string|null} - Section content or null if not found
64
+ */
65
+ function extractSection(content, heading) {
66
+ // Escape special regex characters in heading
67
+ const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+
69
+ // Match ## heading or ### heading
70
+ const regex = new RegExp(
71
+ `^#{2,3}\\s*${escapedHeading}\\s*$\\n([\\s\\S]*?)(?=^#{2,3}\\s|$)`,
72
+ 'mi'
73
+ );
74
+
75
+ const match = content.match(regex);
76
+ if (match && match[1]) {
77
+ return match[1].trim();
78
+ }
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Extract basic agent info using regex fallback
84
+ * @param {string} content - Raw markdown content
85
+ * @returns {object} - Extracted agent data
86
+ */
87
+ function extractAgentInfoFallback(content) {
88
+ const agent = {};
89
+
90
+ // Try to extract agent name
91
+ const nameMatch = content.match(/name:\s*([^\n]+)/);
92
+ if (nameMatch) agent.name = nameMatch[1].trim();
93
+
94
+ // Try to extract id
95
+ const idMatch = content.match(/id:\s*([^\n]+)/);
96
+ if (idMatch) agent.id = idMatch[1].trim();
97
+
98
+ // Try to extract title
99
+ const titleMatch = content.match(/title:\s*([^\n]+)/);
100
+ if (titleMatch) agent.title = titleMatch[1].trim();
101
+
102
+ // Try to extract icon
103
+ const iconMatch = content.match(/icon:\s*([^\n]+)/);
104
+ if (iconMatch) agent.icon = iconMatch[1].trim();
105
+
106
+ // Try to extract whenToUse - handle apostrophes within text (e.g., "don't")
107
+ // First try quoted string, then unquoted to end of line
108
+ const whenMatchQuoted = content.match(/whenToUse:\s*["'](.+?)["'](?:\n|$)/);
109
+ const whenMatchUnquoted = content.match(/whenToUse:\s*([^\n]+)/);
110
+ const whenMatch = whenMatchQuoted || whenMatchUnquoted;
111
+ if (whenMatch) agent.whenToUse = whenMatch[1].trim();
112
+
113
+ return Object.keys(agent).length > 0 ? agent : null;
114
+ }
115
+
116
+ /**
117
+ * Parse a single agent file
118
+ * @param {string} filePath - Path to agent markdown file
119
+ * @returns {object} - Parsed agent data
120
+ */
121
+ function parseAgentFile(filePath) {
122
+ const result = {
123
+ path: filePath,
124
+ filename: path.basename(filePath),
125
+ id: path.basename(filePath, '.md'),
126
+ raw: null,
127
+ yaml: null,
128
+ agent: null,
129
+ persona_profile: null,
130
+ commands: [],
131
+ dependencies: null,
132
+ sections: {
133
+ quickCommands: null,
134
+ collaboration: null,
135
+ guide: null,
136
+ },
137
+ error: null,
138
+ };
139
+
140
+ try {
141
+ // Read file content
142
+ const content = fs.readFileSync(filePath, 'utf8');
143
+ result.raw = content;
144
+
145
+ // Extract YAML block
146
+ const yamlContent = extractYamlBlock(content);
147
+ if (!yamlContent) {
148
+ result.error = 'No YAML block found';
149
+ return result;
150
+ }
151
+
152
+ // Parse YAML
153
+ const parsed = parseYaml(yamlContent);
154
+ if (!parsed) {
155
+ // Try fallback extraction for basic agent info
156
+ const fallbackAgent = extractAgentInfoFallback(content);
157
+ if (fallbackAgent) {
158
+ result.agent = fallbackAgent;
159
+ result.error = 'YAML parse failed, using fallback extraction';
160
+ // Don't return - allow processing with partial data
161
+ } else {
162
+ result.error = 'Failed to parse YAML';
163
+ return result;
164
+ }
165
+ } else {
166
+ result.yaml = parsed;
167
+
168
+ // Extract key sections
169
+ result.agent = parsed.agent || null;
170
+ result.persona_profile = parsed.persona_profile || null;
171
+ result.commands = parsed.commands || [];
172
+ result.dependencies = parsed.dependencies || null;
173
+ }
174
+
175
+ // Extract markdown sections (always try)
176
+ result.sections.quickCommands = extractSection(content, 'Quick Commands');
177
+ result.sections.collaboration = extractSection(content, 'Agent Collaboration');
178
+ result.sections.guide = extractSection(content, 'Developer Guide') ||
179
+ extractSection(content, '.*Guide \\(\\*guide command\\)');
180
+
181
+ } catch (error) {
182
+ result.error = error.message;
183
+ }
184
+
185
+ return result;
186
+ }
187
+
188
+ /**
189
+ * Parse all agent files in a directory
190
+ * @param {string} agentsDir - Path to agents directory
191
+ * @returns {object[]} - Array of parsed agent data
192
+ */
193
+ function parseAllAgents(agentsDir) {
194
+ const agents = [];
195
+
196
+ if (!fs.existsSync(agentsDir)) {
197
+ console.error(`Agents directory not found: ${agentsDir}`);
198
+ return agents;
199
+ }
200
+
201
+ const files = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
202
+
203
+ for (const file of files) {
204
+ const filePath = path.join(agentsDir, file);
205
+ const agentData = parseAgentFile(filePath);
206
+ agents.push(agentData);
207
+ }
208
+
209
+ return agents;
210
+ }
211
+
212
+ /**
213
+ * Get visibility-filtered commands
214
+ * @param {object[]} commands - Array of command objects
215
+ * @param {string} visibility - Visibility level (full, quick, key)
216
+ * @returns {object[]} - Filtered commands
217
+ */
218
+ function getVisibleCommands(commands, visibility) {
219
+ if (!Array.isArray(commands)) return [];
220
+
221
+ return commands.filter(cmd => {
222
+ if (!cmd.visibility) return true; // Include if no visibility defined
223
+ return cmd.visibility.includes(visibility);
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Format commands as bullet list
229
+ * @param {object[]} commands - Array of command objects
230
+ * @param {string} prefix - Prefix for command name (default: '*')
231
+ * @returns {string} - Formatted bullet list
232
+ */
233
+ function formatCommandsList(commands, prefix = '*') {
234
+ if (!Array.isArray(commands) || commands.length === 0) {
235
+ return '- No commands available';
236
+ }
237
+
238
+ return commands
239
+ .map(cmd => `- \`${prefix}${cmd.name}\` - ${cmd.description || 'No description'}`)
240
+ .join('\n');
241
+ }
242
+
243
+ module.exports = {
244
+ extractYamlBlock,
245
+ parseYaml,
246
+ extractSection,
247
+ parseAgentFile,
248
+ parseAllAgents,
249
+ getVisibleCommands,
250
+ formatCommandsList,
251
+ };