aios-core 2.3.1 → 3.1.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.
@@ -262,22 +262,209 @@ function extractStoryInfo(storyPath) {
262
262
  }
263
263
  ```
264
264
 
265
- ### Step 4: Generate PR Title
265
+ ### Step 4: Generate PR Title (Configurable Format)
266
+
267
+ > **Configuration-Driven:** PR title format is controlled by `core-config.yaml` → `github.pr.title_format`
268
+ > This allows each project to choose the format that matches their workflow.
266
269
 
267
270
  ```javascript
271
+ const yaml = require('js-yaml');
272
+ const fs = require('fs');
273
+ const path = require('path');
274
+
275
+ /**
276
+ * Load PR configuration from core-config.yaml
277
+ * @returns {Object} PR configuration with defaults
278
+ */
279
+ function loadPRConfig() {
280
+ const configPath = path.join(process.cwd(), '.aios-core', 'core-config.yaml');
281
+
282
+ // Default configuration (for projects without core-config)
283
+ const defaults = {
284
+ title_format: 'story-first', // Safe default for most projects
285
+ include_story_id: true,
286
+ conventional_commits: {
287
+ enabled: false,
288
+ branch_type_map: {
289
+ 'feature/': 'feat',
290
+ 'feat/': 'feat',
291
+ 'fix/': 'fix',
292
+ 'bugfix/': 'fix',
293
+ 'hotfix/': 'fix',
294
+ 'docs/': 'docs',
295
+ 'chore/': 'chore',
296
+ 'refactor/': 'refactor',
297
+ 'test/': 'test',
298
+ 'perf/': 'perf',
299
+ 'ci/': 'ci',
300
+ 'style/': 'style',
301
+ 'build/': 'build'
302
+ },
303
+ default_type: 'feat'
304
+ }
305
+ };
306
+
307
+ try {
308
+ if (fs.existsSync(configPath)) {
309
+ const config = yaml.load(fs.readFileSync(configPath, 'utf8'));
310
+ return { ...defaults, ...config?.github?.pr };
311
+ }
312
+ } catch (error) {
313
+ console.warn('Could not load core-config.yaml, using defaults');
314
+ }
315
+
316
+ return defaults;
317
+ }
318
+
319
+ /**
320
+ * Generate PR title based on project configuration.
321
+ *
322
+ * Supported formats (configured in core-config.yaml → github.pr.title_format):
323
+ *
324
+ * 1. "conventional" - Conventional Commits format (for semantic-release)
325
+ * Example: "feat(auth): implement OAuth login [Story 6.17]"
326
+ *
327
+ * 2. "story-first" - Story ID first (legacy/simple projects)
328
+ * Example: "[Story 6.17] Implement OAuth Login"
329
+ *
330
+ * 3. "branch-based" - Branch name converted to title
331
+ * Example: "Feature User Auth"
332
+ *
333
+ * @param {string} branchName - Current git branch name
334
+ * @param {Object} storyInfo - Story information (id, title)
335
+ * @returns {string} Formatted PR title
336
+ */
268
337
  function generatePRTitle(branchName, storyInfo) {
338
+ const config = loadPRConfig();
339
+ const format = config.title_format || 'story-first';
340
+
341
+ switch (format) {
342
+ case 'conventional':
343
+ return generateConventionalTitle(branchName, storyInfo, config);
344
+ case 'story-first':
345
+ return generateStoryFirstTitle(branchName, storyInfo, config);
346
+ case 'branch-based':
347
+ return generateBranchBasedTitle(branchName, storyInfo, config);
348
+ default:
349
+ return generateStoryFirstTitle(branchName, storyInfo, config);
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Format: {type}({scope}): {description} [Story {id}]
355
+ * Used for: Projects with semantic-release automation
356
+ */
357
+ function generateConventionalTitle(branchName, storyInfo, config) {
358
+ const typeMap = config.conventional_commits?.branch_type_map || {};
359
+ const defaultType = config.conventional_commits?.default_type || 'feat';
360
+
361
+ // Detect commit type from branch prefix
362
+ let type = defaultType;
363
+ for (const [prefix, commitType] of Object.entries(typeMap)) {
364
+ if (branchName.startsWith(prefix)) {
365
+ type = commitType;
366
+ break;
367
+ }
368
+ }
369
+
370
+ // Extract scope from branch name (e.g., feat/auth/login -> scope=auth)
371
+ const scopeMatch = branchName.match(/^[a-z-]+\/([a-z-]+)\//);
372
+ const scope = scopeMatch ? scopeMatch[1] : null;
373
+ const scopeStr = scope ? `(${scope})` : '';
374
+
375
+ // Generate description
376
+ if (storyInfo && storyInfo.id && storyInfo.title) {
377
+ let cleanTitle = storyInfo.title
378
+ .replace(/^Story\s*\d+\.\d+[:\s-]*/i, '')
379
+ .trim();
380
+ cleanTitle = cleanTitle.charAt(0).toLowerCase() + cleanTitle.slice(1);
381
+
382
+ const storyRef = config.include_story_id ? ` [Story ${storyInfo.id}]` : '';
383
+ return `${type}${scopeStr}: ${cleanTitle}${storyRef}`;
384
+ }
385
+
386
+ // Fallback: convert branch name to description
387
+ const description = branchName
388
+ .replace(/^(feature|feat|fix|bugfix|hotfix|docs|chore|refactor|test|perf|ci|style|build)\//, '')
389
+ .replace(/^[a-z-]+\//, '')
390
+ .replace(/-/g, ' ')
391
+ .toLowerCase()
392
+ .trim();
393
+
394
+ return `${type}${scopeStr}: ${description}`;
395
+ }
396
+
397
+ /**
398
+ * Format: [Story {id}] {Title}
399
+ * Used for: Simple projects, legacy workflows, non-NPM projects
400
+ */
401
+ function generateStoryFirstTitle(branchName, storyInfo, config) {
269
402
  if (storyInfo && storyInfo.id && storyInfo.title) {
270
403
  return `[Story ${storyInfo.id}] ${storyInfo.title}`;
271
404
  }
272
405
 
273
406
  // Fallback: convert branch name to title
274
407
  return branchName
275
- .replace(/^feature\//, '')
408
+ .replace(/^(feature|feat|fix|bugfix|hotfix|docs|chore|refactor|test|perf|ci|style|build)\//, '')
276
409
  .replace(/-/g, ' ')
277
410
  .replace(/\b\w/g, c => c.toUpperCase());
278
411
  }
412
+
413
+ /**
414
+ * Format: {Branch Name As Title}
415
+ * Used for: Minimal projects, quick iterations
416
+ */
417
+ function generateBranchBasedTitle(branchName, storyInfo, config) {
418
+ const title = branchName
419
+ .replace(/^(feature|feat|fix|bugfix|hotfix|docs|chore|refactor|test|perf|ci|style|build)\//, '')
420
+ .replace(/-/g, ' ')
421
+ .replace(/\b\w/g, c => c.toUpperCase());
422
+
423
+ if (config.include_story_id && storyInfo?.id) {
424
+ return `${title} [Story ${storyInfo.id}]`;
425
+ }
426
+
427
+ return title;
428
+ }
429
+ ```
430
+
431
+ ## Configuration Reference
432
+
433
+ Add to your project's `core-config.yaml`:
434
+
435
+ ```yaml
436
+ github:
437
+ pr:
438
+ # Options: conventional | story-first | branch-based
439
+ title_format: conventional # For semantic-release projects
440
+ # title_format: story-first # For simple projects (default)
441
+
442
+ include_story_id: true
443
+
444
+ conventional_commits:
445
+ enabled: true
446
+ branch_type_map:
447
+ feature/: feat
448
+ fix/: fix
449
+ docs/: docs
450
+ # Add custom mappings as needed
451
+ default_type: feat
452
+
453
+ semantic_release:
454
+ enabled: true # Set false if not using semantic-release
279
455
  ```
280
456
 
457
+ ## Title Format Examples
458
+
459
+ | Format | Branch | Story | Generated Title |
460
+ |--------|--------|-------|-----------------|
461
+ | `conventional` | `feature/user-auth` | 6.17: User Auth | `feat: user auth [Story 6.17]` |
462
+ | `conventional` | `fix/cli/parsing` | 6.18: CLI Fix | `fix(cli): cLI fix [Story 6.18]` |
463
+ | `story-first` | `feature/user-auth` | 6.17: User Auth | `[Story 6.17] User Auth` |
464
+ | `story-first` | `fix/cli-bug` | - | `Cli Bug` |
465
+ | `branch-based` | `feature/user-auth` | 6.17 | `User Auth [Story 6.17]` |
466
+ | `branch-based` | `docs/readme` | - | `Readme` |
467
+
281
468
  ### Step 5: Generate PR Description
282
469
 
283
470
  ```javascript
@@ -415,10 +602,60 @@ Called by `@github-devops` via `*create-pr` command.
415
602
  ## Validation
416
603
 
417
604
  - PR created in correct repository (detected URL)
418
- - PR title includes story ID if available
605
+ - PR title follows Conventional Commits format (required for semantic-release)
606
+ - PR title includes story ID if available (e.g., `[Story 6.17]`)
419
607
  - PR description includes repository context
420
608
  - Base branch is correct (usually main/master)
421
609
 
610
+ ## Semantic-Release Integration (Optional)
611
+
612
+ > **Note:** This section only applies when `core-config.yaml` has:
613
+ > - `github.pr.title_format: conventional`
614
+ > - `github.semantic_release.enabled: true`
615
+ >
616
+ > Projects without semantic-release should use `title_format: story-first` (default).
617
+
618
+ **When enabled:** PRs merged via "Squash and merge" use the PR title as commit message, triggering semantic-release:
619
+
620
+ | Branch Pattern | Generated Title | Release |
621
+ |---------------|-----------------|---------|
622
+ | `feature/user-auth` | `feat: user auth` | ✅ Minor |
623
+ | `feat/auth/sso-login` | `feat(auth): sso login` | ✅ Minor |
624
+ | `fix/cli-parsing` | `fix: cli parsing` | ✅ Patch |
625
+ | `docs/readme-update` | `docs: readme update` | ❌ None |
626
+ | `chore/deps-update` | `chore: deps update` | ❌ None |
627
+
628
+ For breaking changes, manually edit the PR title to include `!`:
629
+ - `feat!: redesign authentication API [Story 7.1]`
630
+
631
+ ## Configuration for Different Project Types
632
+
633
+ ### NPM Package with Semantic-Release (aios-core)
634
+ ```yaml
635
+ github:
636
+ pr:
637
+ title_format: conventional
638
+ semantic_release:
639
+ enabled: true
640
+ ```
641
+
642
+ ### Simple Web App (no releases)
643
+ ```yaml
644
+ github:
645
+ pr:
646
+ title_format: story-first # [Story 6.17] Title
647
+ semantic_release:
648
+ enabled: false
649
+ ```
650
+
651
+ ### Quick Prototypes
652
+ ```yaml
653
+ github:
654
+ pr:
655
+ title_format: branch-based # Just branch name as title
656
+ include_story_id: false
657
+ ```
658
+
422
659
  ## Notes
423
660
 
424
661
  - Works with ANY repository
@@ -0,0 +1,289 @@
1
+ ---
2
+ task: Create Squad
3
+ responsavel: "@squad-creator"
4
+ responsavel_type: agent
5
+ atomic_layer: task
6
+ Entrada: |
7
+ - name: Nome do squad (kebab-case, obrigatorio)
8
+ - description: Descricao (opcional, elicitacao)
9
+ - author: Autor (opcional, default: git config user.name)
10
+ - license: Licenca (opcional, default: MIT)
11
+ - template: Template base (basic | etl | agent-only)
12
+ - config_mode: extend | override | none
13
+ Saida: |
14
+ - squad_path: Caminho do squad criado
15
+ - manifest: Conteudo do squad.yaml gerado
16
+ - next_steps: Instrucoes para proximos passos
17
+ Checklist:
18
+ - "[ ] Validar nome (kebab-case, nao existe)"
19
+ - "[ ] Coletar informacoes via elicitacao"
20
+ - "[ ] Gerar estrutura de diretorios"
21
+ - "[ ] Gerar squad.yaml"
22
+ - "[ ] Gerar arquivos de config (coding-standards, etc.)"
23
+ - "[ ] Gerar exemplo de agent"
24
+ - "[ ] Gerar exemplo de task"
25
+ - "[ ] Executar validacao inicial"
26
+ - "[ ] Exibir proximos passos"
27
+ ---
28
+
29
+ # *create-squad
30
+
31
+ Cria um novo squad seguindo a arquitetura task-first do AIOS.
32
+
33
+ ## Uso
34
+
35
+ ```
36
+ @squad-creator
37
+
38
+ *create-squad
39
+ # → Modo interativo, elicita todas as informacoes
40
+
41
+ *create-squad meu-squad
42
+ # → Usa defaults para o resto
43
+
44
+ *create-squad meu-squad --template etl --author "Meu Nome"
45
+ # → Especifica opcoes diretamente
46
+ ```
47
+
48
+ ## Parametros
49
+
50
+ | Parameter | Type | Default | Description |
51
+ |-----------|------|---------|-------------|
52
+ | `name` | string | - | Squad name (kebab-case, required) |
53
+ | `--description` | string | "Custom squad" | Squad description |
54
+ | `--author` | string | git user.name | Author name |
55
+ | `--license` | string | MIT | License type |
56
+ | `--template` | string | basic | Template: basic, etl, agent-only |
57
+ | `--config-mode` | string | extend | Config inheritance: extend, override, none |
58
+ | `--skip-validation` | flag | false | Skip initial validation |
59
+ | `--yes` | flag | false | Skip interactive prompts, use defaults |
60
+
61
+ ## Elicitacao Interativa
62
+
63
+ ```
64
+ ? Squad name: meu-dominio-squad
65
+ ? Description: Squad para automacao de processos X
66
+ ? Author: [git config user.name]
67
+ ? License: (MIT)
68
+ > MIT
69
+ Apache-2.0
70
+ ISC
71
+ UNLICENSED
72
+ ? Template:
73
+ > basic (estrutura minima)
74
+ etl (processamento de dados)
75
+ agent-only (apenas agentes)
76
+ ? Include example agent? (Y/n)
77
+ ? Include example task? (Y/n)
78
+ ? Config inheritance:
79
+ > extend (adiciona as regras do core)
80
+ override (substitui regras do core)
81
+ none (sem heranca)
82
+ ? Minimum AIOS version: (2.1.0)
83
+ ```
84
+
85
+ ## Templates Disponiveis
86
+
87
+ | Template | Description | Components |
88
+ |----------|-------------|------------|
89
+ | `basic` | Estrutura minima | 1 agent, 1 task |
90
+ | `etl` | Processamento de dados | 2 agents, 3 tasks, scripts |
91
+ | `agent-only` | Apenas agentes | 2 agents, sem tasks |
92
+
93
+ ## Estrutura Gerada
94
+
95
+ ```
96
+ ./squads/meu-dominio-squad/
97
+ ├── squad.yaml # Manifest
98
+ ├── README.md # Documentacao
99
+ ├── config/
100
+ │ ├── coding-standards.md # Extends/override core
101
+ │ ├── tech-stack.md # Tecnologias do squad
102
+ │ └── source-tree.md # Estrutura documentada
103
+ ├── agents/
104
+ │ └── example-agent.md # Agente de exemplo
105
+ ├── tasks/
106
+ │ └── example-agent-task.md # Task de exemplo
107
+ ├── checklists/
108
+ │ └── .gitkeep
109
+ ├── workflows/
110
+ │ └── .gitkeep
111
+ ├── templates/
112
+ │ └── .gitkeep
113
+ ├── tools/
114
+ │ └── .gitkeep
115
+ ├── scripts/
116
+ │ └── .gitkeep
117
+ └── data/
118
+ └── .gitkeep
119
+ ```
120
+
121
+ ## squad.yaml Gerado
122
+
123
+ ```yaml
124
+ name: meu-dominio-squad
125
+ version: 1.0.0
126
+ description: Squad para automacao de processos X
127
+ author: Meu Nome
128
+ license: MIT
129
+ slashPrefix: meu-dominio
130
+
131
+ aios:
132
+ minVersion: "2.1.0"
133
+ type: squad
134
+
135
+ components:
136
+ tasks:
137
+ - example-agent-task.md
138
+ agents:
139
+ - example-agent.md
140
+ workflows: []
141
+ checklists: []
142
+ templates: []
143
+ tools: []
144
+ scripts: []
145
+
146
+ config:
147
+ extends: extend
148
+ coding-standards: config/coding-standards.md
149
+ tech-stack: config/tech-stack.md
150
+ source-tree: config/source-tree.md
151
+
152
+ dependencies:
153
+ node: []
154
+ python: []
155
+ squads: []
156
+
157
+ tags:
158
+ - custom
159
+ - automation
160
+ ```
161
+
162
+ ## Flow
163
+
164
+ ```
165
+ 1. Parse arguments
166
+ ├── If name provided → validate kebab-case
167
+ └── If no name → prompt for name
168
+
169
+ 2. Check if squad exists
170
+ ├── If exists → error with suggestion
171
+ └── If not exists → continue
172
+
173
+ 3. Collect configuration
174
+ ├── If --yes flag → use all defaults
175
+ └── If interactive → elicit each option
176
+
177
+ 4. Generate squad structure
178
+ ├── Create directories
179
+ ├── Generate squad.yaml from template
180
+ ├── Generate config files
181
+ ├── Generate example agent (if requested)
182
+ ├── Generate example task (if requested)
183
+ └── Add .gitkeep to empty directories
184
+
185
+ 5. Run initial validation
186
+ ├── If --skip-validation → skip
187
+ └── If validation → run squad-validator
188
+
189
+ 6. Display success message
190
+ └── Show next steps
191
+ ```
192
+
193
+ ## Output de Sucesso
194
+
195
+ ```
196
+ ✅ Squad created successfully!
197
+
198
+ 📁 Location: ./squads/meu-dominio-squad/
199
+
200
+ 📋 Next steps:
201
+ 1. cd squads/meu-dominio-squad
202
+ 2. Customize squad.yaml with your details
203
+ 3. Create your agents in agents/
204
+ 4. Create tasks in tasks/ (task-first!)
205
+ 5. Validate: @squad-creator *validate-squad meu-dominio-squad
206
+
207
+ 📚 Documentation:
208
+ - Squad Guide: docs/guides/squads-guide.md
209
+ - Task Format: .aios-core/docs/standards/TASK-FORMAT-SPECIFICATION-V1.md
210
+
211
+ 🚀 When ready to share:
212
+ - Local only: Keep in ./squads/ (private)
213
+ - Public: @squad-creator *publish-squad meu-dominio-squad
214
+ - API: @squad-creator *sync-squad-synkra meu-dominio-squad
215
+ ```
216
+
217
+ ## Error Handling
218
+
219
+ | Error | Cause | Resolution |
220
+ |-------|-------|------------|
221
+ | `INVALID_NAME` | Name not kebab-case | Use lowercase with hyphens |
222
+ | `SQUAD_EXISTS` | Squad already exists | Choose different name or delete existing |
223
+ | `PERMISSION_DENIED` | Can't write to squads/ | Check directory permissions |
224
+ | `VALIDATION_FAILED` | Generated squad invalid | Check error details, fix manually |
225
+
226
+ ## Implementation
227
+
228
+ ```javascript
229
+ const { SquadGenerator } = require('./.aios-core/development/scripts/squad');
230
+ const { SquadValidator } = require('./.aios-core/development/scripts/squad');
231
+
232
+ async function createSquad(options) {
233
+ const {
234
+ name,
235
+ description,
236
+ author,
237
+ license,
238
+ template,
239
+ configMode,
240
+ skipValidation,
241
+ includeAgent,
242
+ includeTask,
243
+ aiosMinVersion
244
+ } = options;
245
+
246
+ // Validate name
247
+ if (!/^[a-z][a-z0-9-]*[a-z0-9]$/.test(name)) {
248
+ throw new Error('INVALID_NAME: Squad name must be kebab-case');
249
+ }
250
+
251
+ // Generate squad
252
+ const generator = new SquadGenerator();
253
+ const result = await generator.generate({
254
+ name,
255
+ description,
256
+ author,
257
+ license,
258
+ template,
259
+ configMode,
260
+ includeAgent,
261
+ includeTask,
262
+ aiosMinVersion
263
+ });
264
+
265
+ // Validate (unless skipped)
266
+ if (!skipValidation) {
267
+ const validator = new SquadValidator();
268
+ const validation = await validator.validate(result.path);
269
+ if (!validation.valid) {
270
+ console.warn('Warning: Generated squad has validation issues');
271
+ console.warn(validator.formatResult(validation, result.path));
272
+ }
273
+ }
274
+
275
+ // Display success
276
+ console.log(`\n✅ Squad created successfully!\n`);
277
+ console.log(`📁 Location: ${result.path}/\n`);
278
+ displayNextSteps(name);
279
+
280
+ return result;
281
+ }
282
+ ```
283
+
284
+ ## Related
285
+
286
+ - **Agent:** @squad-creator (Craft)
287
+ - **Script:** squad-generator.js
288
+ - **Validator:** squad-validator.js (SQS-3)
289
+ - **Loader:** squad-loader.js (SQS-2)