aios-core 2.2.1 → 2.3.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 (108) hide show
  1. package/.aios-core/.session/current-session.json +14 -14
  2. package/.aios-core/cli/commands/migrate/validate.js +1 -1
  3. package/.aios-core/core/docs/session-update-pattern.md +17 -10
  4. package/.aios-core/core/elicitation/elicitation-engine.js +11 -6
  5. package/.aios-core/core/elicitation/session-manager.js +2 -1
  6. package/.aios-core/core/registry/registry-schema.json +166 -166
  7. package/.aios-core/core/registry/service-registry.json +6585 -6585
  8. package/.aios-core/core-config.yaml +12 -1
  9. package/.aios-core/data/agent-config-requirements.yaml +5 -5
  10. package/.aios-core/development/agents/devops.md +12 -0
  11. package/.aios-core/development/scripts/squad/README.md +112 -0
  12. package/.aios-core/development/scripts/squad/index.js +41 -0
  13. package/.aios-core/development/scripts/squad/squad-loader.js +359 -0
  14. package/.aios-core/development/scripts/squad/squad-validator.js +685 -0
  15. package/.aios-core/development/tasks/add-mcp.md +11 -5
  16. package/.aios-core/development/tasks/search-mcp.md +309 -0
  17. package/.aios-core/development/tasks/setup-mcp-docker.md +11 -8
  18. package/.aios-core/development/tasks/squad-creator-validate.md +151 -0
  19. package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +3 -3
  20. package/.aios-core/index.d.ts +7 -7
  21. package/.aios-core/index.js +1 -1
  22. package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
  23. package/.aios-core/infrastructure/scripts/component-generator.js +1 -1
  24. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  25. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  26. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  27. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  28. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  29. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  30. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  31. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  32. package/.aios-core/infrastructure/tests/utilities-audit-results.json +500 -500
  33. package/.aios-core/infrastructure/tools/README.md +1 -1
  34. package/.aios-core/install-manifest.yaml +4 -1
  35. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  36. package/.aios-core/manifests/workers.csv +203 -203
  37. package/.aios-core/package.json +102 -102
  38. package/.aios-core/product/templates/activation-instructions-template.md +7 -7
  39. package/.aios-core/product/templates/adr.hbs +125 -125
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/dbdr.hbs +241 -241
  42. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  43. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  44. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  46. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  47. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  48. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  49. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  50. package/.aios-core/product/templates/epic.hbs +212 -212
  51. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  52. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  53. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  54. package/.aios-core/product/templates/pmdr.hbs +186 -186
  55. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  56. package/.aios-core/product/templates/prd.hbs +201 -201
  57. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  58. package/.aios-core/product/templates/story.hbs +263 -263
  59. package/.aios-core/product/templates/task.hbs +170 -170
  60. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  61. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  62. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  63. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  64. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  65. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  66. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  67. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  68. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  69. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  70. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  71. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  72. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  73. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  74. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  75. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  76. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  77. package/.aios-core/schemas/squad-schema.json +185 -0
  78. package/.aios-core/scripts/README.md +90 -322
  79. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  80. package/.claude/rules/mcp-usage.md +116 -100
  81. package/LICENSE +48 -48
  82. package/README.md +3 -4
  83. package/bin/aios-init.js +11 -6
  84. package/bin/aios.js +2 -1
  85. package/package.json +2 -3
  86. package/packages/installer/package.json +39 -39
  87. package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
  88. package/packages/installer/tests/unit/env-template.test.js +4 -3
  89. package/templates/squad/LICENSE +21 -21
  90. package/templates/squad/README.md +37 -37
  91. package/templates/squad/agents/example-agent.yaml +36 -36
  92. package/templates/squad/package.json +19 -19
  93. package/templates/squad/squad.yaml +25 -25
  94. package/templates/squad/tasks/example-task.yaml +46 -46
  95. package/templates/squad/templates/example-template.md +24 -24
  96. package/templates/squad/tests/example-agent.test.js +53 -53
  97. package/templates/squad/workflows/example-workflow.yaml +54 -54
  98. package/tools/diagnose-npx-issue.ps1 +96 -96
  99. package/tools/quick-diagnose.cmd +85 -85
  100. package/tools/quick-diagnose.ps1 +117 -117
  101. package/.aios-core/core/data/agent-config-requirements.yaml +0 -368
  102. package/.aios-core/core/data/aios-kb.md +0 -924
  103. package/.aios-core/core/data/workflow-patterns.yaml +0 -267
  104. package/.aios-core/product/templates/1mcp-config.yaml +0 -225
  105. package/.aios-core/scripts/context-detector.js +0 -226
  106. package/.aios-core/scripts/elicitation-engine.js +0 -385
  107. package/.aios-core/scripts/elicitation-session-manager.js +0 -300
  108. package/.claude/CLAUDE.md +0 -221
@@ -69,7 +69,18 @@ slashPrefix: AIOS
69
69
  # SECTION 3: Resource Locations
70
70
  # ============================================
71
71
  toolsLocation: .aios-core/tools
72
- scriptsLocation: .aios-core/scripts
72
+
73
+ # Scripts are organized into modules by domain (Story 6.16)
74
+ # - core: Core framework modules (elicitation, session)
75
+ # - development: Development scripts (greeting, workflow, exit hooks)
76
+ # - infrastructure: Infrastructure scripts (git config, project status, validators)
77
+ # - legacy: Migration utilities only (deprecated scripts have been removed)
78
+ scriptsLocation:
79
+ core: .aios-core/core
80
+ development: .aios-core/development/scripts
81
+ infrastructure: .aios-core/infrastructure/scripts
82
+ legacy: .aios-core/scripts
83
+
73
84
  dataLocation: .aios-core/data
74
85
  elicitationLocation: .aios-core/elicitation
75
86
  # Squads replaced Squads in OSR-8 - see docs/guides/squads-guide.md
@@ -64,10 +64,10 @@ agents:
64
64
  - path: .aios-core/data/technical-preferences.md
65
65
  lazy: false
66
66
  size: 15KB
67
- - path: .aios-core/data/test-levels-framework.md
67
+ - path: .aios-core/product/data/test-levels-framework.md
68
68
  lazy: false
69
69
  size: 8KB
70
- - path: .aios-core/data/test-priorities-matrix.md
70
+ - path: .aios-core/product/data/test-priorities-matrix.md
71
71
  lazy: false
72
72
  size: 6KB
73
73
  lazy_loading:
@@ -120,7 +120,7 @@ agents:
120
120
  - storyBacklog
121
121
  - templatesLocation
122
122
  files_loaded:
123
- - path: .aios-core/data/elicitation-methods.md
123
+ - path: .aios-core/product/data/elicitation-methods.md
124
124
  lazy: false
125
125
  size: 5KB
126
126
  lazy_loading:
@@ -134,7 +134,7 @@ agents:
134
134
  - storyBacklog
135
135
  - dataLocation
136
136
  files_loaded:
137
- - path: .aios-core/data/mode-selection-best-practices.md
137
+ - path: .aios-core/product/data/mode-selection-best-practices.md
138
138
  lazy: false
139
139
  size: 10KB
140
140
  - path: .aios-core/data/workflow-patterns.yaml
@@ -182,7 +182,7 @@ agents:
182
182
  - dataLocation
183
183
  - analyticsLocation
184
184
  files_loaded:
185
- - path: .aios-core/data/brainstorming-techniques.md
185
+ - path: .aios-core/product/data/brainstorming-techniques.md
186
186
  lazy: false
187
187
  size: 2KB
188
188
  lazy_loading: {}
@@ -158,6 +158,13 @@ commands:
158
158
  - environment-bootstrap: Complete environment setup for new projects (CLIs, auth, Git/GitHub)
159
159
  - setup-github: Configure DevOps infrastructure for user projects (workflows, CodeRabbit, branch protection, secrets) [Story 5.10]
160
160
 
161
+ # MCP Management (via Docker Gateway) [Story 6.14]
162
+ - search-mcp: Search available MCPs in Docker MCP Toolkit catalog
163
+ - add-mcp: Add MCP server to Docker MCP Toolkit
164
+ - list-mcps: List currently enabled MCPs and their tools
165
+ - remove-mcp: Remove MCP server from Docker MCP Toolkit
166
+ - setup-mcp-docker: Initial Docker MCP Toolkit configuration [Story 5.11]
167
+
161
168
  # Utilities
162
169
  - session-info: Show current session details (agent history, commands)
163
170
  - guide: Show comprehensive usage guide for this agent
@@ -173,6 +180,10 @@ dependencies:
173
180
  - ci-cd-configuration.md
174
181
  - github-devops-repository-cleanup.md
175
182
  - release-management.md
183
+ # MCP Management Tasks [Story 6.14]
184
+ - search-mcp.md
185
+ - add-mcp.md
186
+ - setup-mcp-docker.md
176
187
  templates:
177
188
  - github-pr-template.md
178
189
  - github-actions-ci.yml
@@ -191,6 +202,7 @@ dependencies:
191
202
  - coderabbit # Automated code review, pre-PR quality gate
192
203
  - github-cli # PRIMARY TOOL - All GitHub operations
193
204
  - git # ALL operations including push (EXCLUSIVE to this agent)
205
+ - docker-gateway # Docker MCP Toolkit gateway for MCP management [Story 6.14]
194
206
 
195
207
  coderabbit_integration:
196
208
  enabled: true
@@ -0,0 +1,112 @@
1
+ # Squad Scripts Module
2
+
3
+ Utilities for the squad-creator agent to manage squads in AIOS projects.
4
+
5
+ ## Overview
6
+
7
+ This module provides utilities for:
8
+ - **Loading** squad manifests from local directories
9
+ - **Validating** squad structure and configuration (SQS-3)
10
+ - **Generating** new squads from templates (SQS-4)
11
+
12
+ ## Components
13
+
14
+ | File | Story | Description |
15
+ |------|-------|-------------|
16
+ | `squad-loader.js` | SQS-2 | Load and resolve squad manifests |
17
+ | `squad-validator.js` | SQS-3 | Validate squad structure |
18
+ | `squad-generator.js` | SQS-4 | Generate new squads |
19
+
20
+ ## Usage
21
+
22
+ ### Squad Loader
23
+
24
+ ```javascript
25
+ const { SquadLoader } = require('./.aios-core/development/scripts/squad');
26
+
27
+ // Create loader instance
28
+ const loader = new SquadLoader({
29
+ squadsPath: './squads', // Default: './squads'
30
+ verbose: false // Enable debug logging
31
+ });
32
+
33
+ // Resolve squad by name
34
+ const { path, manifestPath } = await loader.resolve('my-squad');
35
+
36
+ // Load and parse manifest
37
+ const manifest = await loader.loadManifest('./squads/my-squad');
38
+
39
+ // List all local squads
40
+ const squads = await loader.listLocal();
41
+ // Returns: [{ name, path, manifestPath }, ...]
42
+ ```
43
+
44
+ ### Error Handling
45
+
46
+ ```javascript
47
+ const { SquadLoader, SquadLoaderError } = require('./.aios-core/development/scripts/squad');
48
+
49
+ try {
50
+ const loader = new SquadLoader();
51
+ await loader.resolve('non-existent-squad');
52
+ } catch (error) {
53
+ if (error instanceof SquadLoaderError) {
54
+ console.error(`Error [${error.code}]: ${error.message}`);
55
+ console.log(`Suggestion: ${error.suggestion}`);
56
+ }
57
+ }
58
+ ```
59
+
60
+ ### Error Codes
61
+
62
+ | Code | Description | Suggestion |
63
+ |------|-------------|------------|
64
+ | `SQUAD_NOT_FOUND` | Squad directory not found | Create squad with: @squad-creator *create-squad {name} |
65
+ | `MANIFEST_NOT_FOUND` | No manifest file in squad | Create squad.yaml in squad directory |
66
+ | `YAML_PARSE_ERROR` | Invalid YAML syntax | Check YAML syntax - use a YAML linter |
67
+ | `PERMISSION_DENIED` | File permission error | Check file permissions: chmod 644 {path} |
68
+
69
+ ## Manifest Files
70
+
71
+ The loader supports two manifest formats:
72
+
73
+ 1. **`squad.yaml`** (preferred) - New standard format
74
+ 2. **`config.yaml`** (deprecated) - Legacy format with console warning
75
+
76
+ ## Integration with squad-creator Agent
77
+
78
+ This module is used by squad-creator agent tasks:
79
+
80
+ - `*create-squad` - Uses loader to check for conflicts
81
+ - `*validate-squad` - Uses loader to load and validate manifest
82
+ - `*list-squads` - Uses loader to enumerate local squads
83
+
84
+ ## Related Stories
85
+
86
+ - **SQS-2**: Squad Loader Utility (this module)
87
+ - **SQS-3**: Squad Validator + Schema
88
+ - **SQS-4**: Squad Creator Agent + Tasks
89
+
90
+ ## Dependencies
91
+
92
+ - `js-yaml` - YAML parsing (project dependency)
93
+ - `fs/promises` - File system operations (Node.js built-in)
94
+ - `path` - Path manipulation (Node.js built-in)
95
+
96
+ ## Testing
97
+
98
+ ```bash
99
+ # Run squad-loader tests
100
+ npm test -- tests/unit/squad/squad-loader.test.js
101
+
102
+ # Run with coverage
103
+ npm test -- tests/unit/squad/squad-loader.test.js --coverage --collectCoverageFrom=".aios-core/development/scripts/squad/*.js"
104
+ ```
105
+
106
+ **Coverage:** 94.5% statements (target: 80%+)
107
+
108
+ ## Version History
109
+
110
+ | Version | Date | Description |
111
+ |---------|------|-------------|
112
+ | 1.0.0 | 2025-12-18 | Initial implementation (Story SQS-2) |
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Squad Scripts Module
3
+ *
4
+ * Central exports for squad-related utilities used by the squad-creator agent.
5
+ *
6
+ * @module squad
7
+ * @see {@link ./squad-loader.js} - Load and resolve squad manifests
8
+ * @see {@link ./squad-validator.js} - Validate squad structure (SQS-3)
9
+ * @see {@link ./squad-generator.js} - Generate new squads (SQS-4)
10
+ */
11
+
12
+ const {
13
+ SquadLoader,
14
+ SquadLoaderError,
15
+ MANIFEST_FILES,
16
+ DEFAULT_SQUADS_PATH,
17
+ ErrorCodes,
18
+ } = require('./squad-loader');
19
+
20
+ const {
21
+ SquadValidator,
22
+ ValidationErrorCodes,
23
+ TASK_REQUIRED_FIELDS,
24
+ } = require('./squad-validator');
25
+
26
+ module.exports = {
27
+ // Squad Loader (SQS-2)
28
+ SquadLoader,
29
+ SquadLoaderError,
30
+ MANIFEST_FILES,
31
+ DEFAULT_SQUADS_PATH,
32
+ ErrorCodes,
33
+
34
+ // Squad Validator (SQS-3)
35
+ SquadValidator,
36
+ ValidationErrorCodes,
37
+ TASK_REQUIRED_FIELDS,
38
+
39
+ // Squad Generator (SQS-4) - TODO: Implement in Story SQS-4
40
+ // SquadGenerator,
41
+ };
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Squad Loader Utility
3
+ *
4
+ * Utilities for loading and resolving squad manifests from local directories.
5
+ * Used by squad-creator agent tasks.
6
+ *
7
+ * @module squad-loader
8
+ * @version 1.0.0
9
+ * @see Story SQS-2: Squad Loader Utility
10
+ */
11
+
12
+ const fs = require('fs').promises;
13
+ const path = require('path');
14
+ const yaml = require('js-yaml');
15
+
16
+ /**
17
+ * Supported manifest file names in order of preference
18
+ * @constant {string[]}
19
+ */
20
+ const MANIFEST_FILES = ['squad.yaml', 'config.yaml'];
21
+
22
+ /**
23
+ * Default path for squads directory
24
+ * @constant {string}
25
+ */
26
+ const DEFAULT_SQUADS_PATH = './squads';
27
+
28
+ /**
29
+ * Error codes for SquadLoaderError
30
+ * @enum {string}
31
+ */
32
+ const ErrorCodes = {
33
+ SQUAD_NOT_FOUND: 'SQUAD_NOT_FOUND',
34
+ MANIFEST_NOT_FOUND: 'MANIFEST_NOT_FOUND',
35
+ YAML_PARSE_ERROR: 'YAML_PARSE_ERROR',
36
+ PERMISSION_DENIED: 'PERMISSION_DENIED',
37
+ };
38
+
39
+ /**
40
+ * Suggestions for each error code
41
+ * @constant {Object.<string, string|Function>}
42
+ */
43
+ const ErrorSuggestions = {
44
+ [ErrorCodes.SQUAD_NOT_FOUND]: (squadName) =>
45
+ `Create squad with: @squad-creator *create-squad ${squadName}`,
46
+ [ErrorCodes.MANIFEST_NOT_FOUND]: () =>
47
+ 'Create squad.yaml in squad directory',
48
+ [ErrorCodes.YAML_PARSE_ERROR]: () =>
49
+ 'Check YAML syntax - use a YAML linter',
50
+ [ErrorCodes.PERMISSION_DENIED]: (filePath) =>
51
+ `Check file permissions: chmod 644 ${filePath}`,
52
+ };
53
+
54
+ /**
55
+ * Custom error class for Squad Loader operations
56
+ * @extends Error
57
+ */
58
+ class SquadLoaderError extends Error {
59
+ /**
60
+ * Create a SquadLoaderError
61
+ * @param {string} code - Error code from ErrorCodes enum
62
+ * @param {string} message - Human-readable error message
63
+ * @param {string} [suggestion] - Suggested fix for the error
64
+ * @param {string} [filePath] - Path to the problematic file/directory
65
+ */
66
+ constructor(code, message, suggestion, filePath) {
67
+ super(message);
68
+ this.name = 'SquadLoaderError';
69
+ this.code = code;
70
+ this.suggestion = suggestion || '';
71
+ this.filePath = filePath || '';
72
+
73
+ // Maintains proper stack trace for where error was thrown (V8 engines)
74
+ if (Error.captureStackTrace) {
75
+ Error.captureStackTrace(this, SquadLoaderError);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Create error for squad not found
81
+ * @param {string} squadName - Name of the squad
82
+ * @param {string} squadsPath - Path searched
83
+ * @returns {SquadLoaderError}
84
+ */
85
+ static squadNotFound(squadName, squadsPath) {
86
+ const filePath = path.join(squadsPath, squadName);
87
+ return new SquadLoaderError(
88
+ ErrorCodes.SQUAD_NOT_FOUND,
89
+ `Squad "${squadName}" not found in ${squadsPath}/`,
90
+ ErrorSuggestions[ErrorCodes.SQUAD_NOT_FOUND](squadName),
91
+ filePath,
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Create error for manifest not found
97
+ * @param {string} squadPath - Path to squad directory
98
+ * @returns {SquadLoaderError}
99
+ */
100
+ static manifestNotFound(squadPath) {
101
+ return new SquadLoaderError(
102
+ ErrorCodes.MANIFEST_NOT_FOUND,
103
+ `No manifest found in ${squadPath}/ (expected squad.yaml or config.yaml)`,
104
+ ErrorSuggestions[ErrorCodes.MANIFEST_NOT_FOUND](),
105
+ squadPath,
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Create error for YAML parse failure
111
+ * @param {string} filePath - Path to the YAML file
112
+ * @param {Error} parseError - Original YAML parse error
113
+ * @returns {SquadLoaderError}
114
+ */
115
+ static yamlParseError(filePath, parseError) {
116
+ return new SquadLoaderError(
117
+ ErrorCodes.YAML_PARSE_ERROR,
118
+ `Failed to parse YAML in ${filePath}: ${parseError.message}`,
119
+ ErrorSuggestions[ErrorCodes.YAML_PARSE_ERROR](),
120
+ filePath,
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Create error for permission denied
126
+ * @param {string} filePath - Path to the file/directory
127
+ * @param {Error} originalError - Original file system error
128
+ * @returns {SquadLoaderError}
129
+ */
130
+ static permissionDenied(filePath, originalError) {
131
+ return new SquadLoaderError(
132
+ ErrorCodes.PERMISSION_DENIED,
133
+ `Permission denied accessing ${filePath}: ${originalError.message}`,
134
+ ErrorSuggestions[ErrorCodes.PERMISSION_DENIED](filePath),
135
+ filePath,
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Returns formatted error string
141
+ * @returns {string}
142
+ */
143
+ toString() {
144
+ let str = `[${this.code}] ${this.message}`;
145
+ if (this.suggestion) {
146
+ str += `\n Suggestion: ${this.suggestion}`;
147
+ }
148
+ return str;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Squad Loader class for loading and resolving squad manifests
154
+ */
155
+ class SquadLoader {
156
+ /**
157
+ * Create a SquadLoader instance
158
+ * @param {Object} [options={}] - Configuration options
159
+ * @param {string} [options.squadsPath='./squads'] - Path to squads directory
160
+ * @param {boolean} [options.verbose=false] - Enable verbose logging
161
+ */
162
+ constructor(options = {}) {
163
+ this.squadsPath = options.squadsPath || DEFAULT_SQUADS_PATH;
164
+ this.verbose = options.verbose || false;
165
+ }
166
+
167
+ /**
168
+ * Log message if verbose mode is enabled
169
+ * @private
170
+ * @param {string} message - Message to log
171
+ */
172
+ _log(message) {
173
+ if (this.verbose) {
174
+ console.log(`[SquadLoader] ${message}`);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Resolve squad path by name
180
+ *
181
+ * Finds a squad directory and its manifest file.
182
+ *
183
+ * @param {string} squadName - Name of the squad (kebab-case)
184
+ * @returns {Promise<{path: string, manifestPath: string}>} Resolved paths
185
+ * @throws {SquadLoaderError} SQUAD_NOT_FOUND if squad directory doesn't exist
186
+ * @throws {SquadLoaderError} MANIFEST_NOT_FOUND if no manifest file found
187
+ *
188
+ * @example
189
+ * const loader = new SquadLoader();
190
+ * const { path, manifestPath } = await loader.resolve('etl-squad');
191
+ * // { path: './squads/etl-squad', manifestPath: './squads/etl-squad/squad.yaml' }
192
+ */
193
+ async resolve(squadName) {
194
+ this._log(`Resolving squad: ${squadName}`);
195
+ const squadPath = path.join(this.squadsPath, squadName);
196
+
197
+ // Check if squad directory exists
198
+ const exists = await this._pathExists(squadPath);
199
+ if (!exists) {
200
+ throw SquadLoaderError.squadNotFound(squadName, this.squadsPath);
201
+ }
202
+
203
+ // Find manifest file
204
+ const manifestPath = await this._findManifest(squadPath);
205
+ if (!manifestPath) {
206
+ throw SquadLoaderError.manifestNotFound(squadPath);
207
+ }
208
+
209
+ this._log(`Resolved: ${squadPath} -> ${manifestPath}`);
210
+ return { path: squadPath, manifestPath };
211
+ }
212
+
213
+ /**
214
+ * Load and parse squad manifest
215
+ *
216
+ * Loads the manifest file from a squad directory and parses it.
217
+ * Shows deprecation warning for config.yaml files.
218
+ *
219
+ * @param {string} squadPath - Path to squad directory
220
+ * @returns {Promise<Object>} Parsed manifest data
221
+ * @throws {SquadLoaderError} MANIFEST_NOT_FOUND if no manifest file found
222
+ * @throws {SquadLoaderError} YAML_PARSE_ERROR if YAML parsing fails
223
+ * @throws {SquadLoaderError} PERMISSION_DENIED if file cannot be read
224
+ *
225
+ * @example
226
+ * const loader = new SquadLoader();
227
+ * const manifest = await loader.loadManifest('./squads/etl-squad');
228
+ * console.log(manifest.name); // 'etl-squad'
229
+ */
230
+ async loadManifest(squadPath) {
231
+ this._log(`Loading manifest from: ${squadPath}`);
232
+
233
+ const manifestPath = await this._findManifest(squadPath);
234
+ if (!manifestPath) {
235
+ throw SquadLoaderError.manifestNotFound(squadPath);
236
+ }
237
+
238
+ // Deprecation warning for config.yaml
239
+ const manifestFilename = path.basename(manifestPath);
240
+ if (manifestFilename === 'config.yaml') {
241
+ console.warn(
242
+ `\u26a0\ufe0f DEPRECATED: ${manifestPath} uses legacy format. Rename to squad.yaml`,
243
+ );
244
+ }
245
+
246
+ try {
247
+ const content = await fs.readFile(manifestPath, 'utf-8');
248
+ const parsed = yaml.load(content);
249
+ this._log(`Manifest loaded successfully: ${manifestPath}`);
250
+ return parsed;
251
+ } catch (error) {
252
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
253
+ throw SquadLoaderError.permissionDenied(manifestPath, error);
254
+ }
255
+ if (error.name === 'YAMLException') {
256
+ throw SquadLoaderError.yamlParseError(manifestPath, error);
257
+ }
258
+ throw error;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * List all local squads in project
264
+ *
265
+ * Scans the squads directory for valid squad directories
266
+ * (directories containing a manifest file).
267
+ *
268
+ * @returns {Promise<Array<{name: string, path: string, manifestPath: string}>>}
269
+ * Array of squad info objects
270
+ *
271
+ * @example
272
+ * const loader = new SquadLoader();
273
+ * const squads = await loader.listLocal();
274
+ * // [
275
+ * // { name: 'etl-squad', path: './squads/etl-squad', manifestPath: '...' },
276
+ * // { name: 'creator-squad', path: './squads/creator-squad', manifestPath: '...' }
277
+ * // ]
278
+ */
279
+ async listLocal() {
280
+ this._log(`Listing squads in: ${this.squadsPath}`);
281
+
282
+ const exists = await this._pathExists(this.squadsPath);
283
+ if (!exists) {
284
+ this._log('Squads directory does not exist, returning empty array');
285
+ return [];
286
+ }
287
+
288
+ let entries;
289
+ try {
290
+ entries = await fs.readdir(this.squadsPath, { withFileTypes: true });
291
+ } catch (error) {
292
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
293
+ throw SquadLoaderError.permissionDenied(this.squadsPath, error);
294
+ }
295
+ throw error;
296
+ }
297
+
298
+ const squads = [];
299
+
300
+ for (const entry of entries) {
301
+ if (entry.isDirectory()) {
302
+ const squadPath = path.join(this.squadsPath, entry.name);
303
+ const manifestPath = await this._findManifest(squadPath);
304
+ if (manifestPath) {
305
+ squads.push({
306
+ name: entry.name,
307
+ path: squadPath,
308
+ manifestPath,
309
+ });
310
+ this._log(`Found squad: ${entry.name}`);
311
+ } else {
312
+ this._log(`Skipped directory (no manifest): ${entry.name}`);
313
+ }
314
+ }
315
+ }
316
+
317
+ this._log(`Found ${squads.length} squad(s)`);
318
+ return squads;
319
+ }
320
+
321
+ /**
322
+ * Find manifest file in squad directory
323
+ * @private
324
+ * @param {string} squadPath - Path to squad directory
325
+ * @returns {Promise<string|null>} Path to manifest or null if not found
326
+ */
327
+ async _findManifest(squadPath) {
328
+ for (const filename of MANIFEST_FILES) {
329
+ const manifestPath = path.join(squadPath, filename);
330
+ if (await this._pathExists(manifestPath)) {
331
+ return manifestPath;
332
+ }
333
+ }
334
+ return null;
335
+ }
336
+
337
+ /**
338
+ * Check if path exists
339
+ * @private
340
+ * @param {string} filePath - Path to check
341
+ * @returns {Promise<boolean>} True if path exists
342
+ */
343
+ async _pathExists(filePath) {
344
+ try {
345
+ await fs.access(filePath);
346
+ return true;
347
+ } catch {
348
+ return false;
349
+ }
350
+ }
351
+ }
352
+
353
+ module.exports = {
354
+ SquadLoader,
355
+ SquadLoaderError,
356
+ MANIFEST_FILES,
357
+ DEFAULT_SQUADS_PATH,
358
+ ErrorCodes,
359
+ };