amazingteam 3.0.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/.ai-team/agents/architect.md +144 -0
- package/.ai-team/agents/ci-analyst.md +188 -0
- package/.ai-team/agents/developer.md +176 -0
- package/.ai-team/agents/planner.md +355 -0
- package/.ai-team/agents/qa.md +189 -0
- package/.ai-team/agents/reviewer.md +211 -0
- package/.ai-team/agents/triage.md +146 -0
- package/.ai-team/commands/ci-analyze.md +116 -0
- package/.ai-team/commands/design.md +100 -0
- package/.ai-team/commands/implement.md +108 -0
- package/.ai-team/commands/release-check.md +142 -0
- package/.ai-team/commands/review.md +142 -0
- package/.ai-team/commands/test.md +115 -0
- package/.ai-team/commands/triage.md +138 -0
- package/.ai-team/memory/architect/architecture_notes.md +67 -0
- package/.ai-team/memory/architect/design_rationale.md +113 -0
- package/.ai-team/memory/architect/module_map.md +84 -0
- package/.ai-team/memory/ci-analyst/failure_patterns.md +102 -0
- package/.ai-team/memory/ci-analyst/runbook_references.md +87 -0
- package/.ai-team/memory/developer/bug_investigation.md +102 -0
- package/.ai-team/memory/developer/build_issues.md +115 -0
- package/.ai-team/memory/developer/implementation_notes.md +83 -0
- package/.ai-team/memory/failures/failure_library.md +103 -0
- package/.ai-team/memory/planner/decomposition_notes.md +82 -0
- package/.ai-team/memory/planner/flow_rules.md +86 -0
- package/.ai-team/memory/planner/github_issue_patterns.md +229 -0
- package/.ai-team/memory/qa/regression_cases.md +101 -0
- package/.ai-team/memory/qa/test_strategy.md +138 -0
- package/.ai-team/memory/qa/validation_notes.md +110 -0
- package/.ai-team/memory/reviewer/quality_rules.md +105 -0
- package/.ai-team/memory/reviewer/recurring_risks.md +109 -0
- package/.ai-team/memory/reviewer/review_notes.md +124 -0
- package/.ai-team/memory/triage/classification_heuristics.md +82 -0
- package/.ai-team/memory/triage/debug_notes.md +87 -0
- package/.ai-team/opencode.template.jsonc +216 -0
- package/.ai-team/skills/bugfix-playbook/skill.md +174 -0
- package/.ai-team/skills/ci-failure-analysis/skill.md +176 -0
- package/.ai-team/skills/issue-triage/skill.md +163 -0
- package/.ai-team/skills/regression-checklist/skill.md +176 -0
- package/.ai-team/skills/release-readiness-check/skill.md +216 -0
- package/.ai-team/skills/repo-architecture-reader/skill.md +139 -0
- package/.ai-team/skills/safe-refactor-checklist/skill.md +215 -0
- package/.ai-team/skills/task-breakdown-and-dispatch/skill.md +151 -0
- package/.ai-team/skills/test-first-feature-dev/skill.md +205 -0
- package/.ai-team/workflows/ci.yml +81 -0
- package/.ai-team/workflows/nightly-ai-maintenance.yml +129 -0
- package/.ai-team/workflows/opencode.yml +33 -0
- package/.ai-team/workflows/pr-check.yml +41 -0
- package/.foundation/foundation.lock +38 -0
- package/.foundation/local-overrides.md +97 -0
- package/.foundation/upgrade-history.md +38 -0
- package/.opencode/agents/architect.md +38 -0
- package/.opencode/agents/ci-analyst.md +38 -0
- package/.opencode/agents/developer.md +43 -0
- package/.opencode/agents/planner.md +47 -0
- package/.opencode/agents/qa.md +34 -0
- package/.opencode/agents/reviewer.md +38 -0
- package/.opencode/agents/triage.md +37 -0
- package/.opencode/commands/auto.md +264 -0
- package/.opencode/commands/breakdown-issue.md +94 -0
- package/.opencode/commands/ci-analyze.md +15 -0
- package/.opencode/commands/close-parent-task.md +122 -0
- package/.opencode/commands/design.md +15 -0
- package/.opencode/commands/dispatch-next.md +102 -0
- package/.opencode/commands/implement.md +16 -0
- package/.opencode/commands/release-check.md +16 -0
- package/.opencode/commands/resume.md +88 -0
- package/.opencode/commands/review.md +15 -0
- package/.opencode/commands/show-blockers.md +97 -0
- package/.opencode/commands/summarize-parent.md +121 -0
- package/.opencode/commands/test.md +15 -0
- package/.opencode/commands/triage.md +109 -0
- package/.opencode/skills/bugfix-playbook/SKILL.md +81 -0
- package/.opencode/skills/ci-failure-analysis/SKILL.md +94 -0
- package/.opencode/skills/issue-triage/SKILL.md +80 -0
- package/.opencode/skills/regression-checklist/SKILL.md +81 -0
- package/.opencode/skills/release-readiness-check/SKILL.md +81 -0
- package/.opencode/skills/repo-architecture-reader/SKILL.md +65 -0
- package/.opencode/skills/safe-refactor-checklist/SKILL.md +76 -0
- package/.opencode/skills/task-breakdown-and-dispatch/SKILL.md +255 -0
- package/.opencode/skills/test-first-feature-dev/SKILL.md +78 -0
- package/AGENTS.md +879 -0
- package/CHANGELOG.md +261 -0
- package/LICENSE +21 -0
- package/README.md +1215 -0
- package/VERSION +1 -0
- package/action/__tests__/downloader.test.js +251 -0
- package/action/__tests__/merger.test.js +156 -0
- package/action/__tests__/path-resolver.test.js +199 -0
- package/action/__tests__/validator.test.js +310 -0
- package/action/action.yml +61 -0
- package/action/index.js +223 -0
- package/action/lib/downloader.js +344 -0
- package/action/lib/merger.js +170 -0
- package/action/lib/path-resolver.js +176 -0
- package/action/lib/setup.js +286 -0
- package/action/lib/validator.js +324 -0
- package/cli/__tests__/cli.test.js +270 -0
- package/cli/amazingteam.cjs +225 -0
- package/cli/commands/check-update.cjs +159 -0
- package/cli/commands/init.cjs +412 -0
- package/cli/commands/local.cjs +264 -0
- package/cli/commands/migrate.cjs +316 -0
- package/cli/commands/status.cjs +241 -0
- package/cli/commands/upgrade.cjs +213 -0
- package/cli/commands/validate.cjs +259 -0
- package/cli/commands/version.cjs +59 -0
- package/cli/sync.cjs +237 -0
- package/dist/index.js +35 -0
- package/docs/architecture/overview.md +138 -0
- package/docs/blocker_resolution_design.md +372 -0
- package/docs/bootstrap-model.md +356 -0
- package/docs/config-reference.md +458 -0
- package/docs/how-to-use.md +178 -0
- package/docs/migration-to-v3.md +355 -0
- package/docs/overlay-guide.md +156 -0
- package/docs/patterns/README.md +67 -0
- package/docs/quick-start-v3.md +330 -0
- package/docs/releases/README.md +64 -0
- package/docs/runbooks/ci/README.md +62 -0
- package/docs/runbooks/ci/build-debug.md +120 -0
- package/docs/runbooks/ci/flaky-tests.md +127 -0
- package/docs/runbooks/getting-started.md +81 -0
- package/docs/upgrade-policy.md +188 -0
- package/docs/versioning.md +199 -0
- package/overlays/README.md +30 -0
- package/overlays/ai-agent-product/.ai-team/skills/llm-integration/skill.md +99 -0
- package/overlays/ai-agent-product/docs/ai-agent-architecture.md +68 -0
- package/overlays/ai-agent-product/overlay.yaml +26 -0
- package/overlays/cpp-qt-desktop/.ai-team/skills/qt-signals-slots/skill.md +60 -0
- package/overlays/cpp-qt-desktop/docs/qt-conventions.md +64 -0
- package/overlays/cpp-qt-desktop/overlay.yaml +22 -0
- package/overlays/python-backend/.ai-team/skills/python-testing/skill.md +90 -0
- package/overlays/python-backend/docs/python-style.md +78 -0
- package/overlays/python-backend/overlay.yaml +22 -0
- package/overlays/web-fullstack/.ai-team/skills/frontend-testing/skill.md +70 -0
- package/overlays/web-fullstack/docs/frontend-conventions.md +68 -0
- package/overlays/web-fullstack/overlay.yaml +26 -0
- package/package.json +84 -0
- package/presets/default.yaml +161 -0
- package/presets/go.yaml +43 -0
- package/presets/python.yaml +43 -0
- package/presets/typescript.yaml +40 -0
- package/schemas/config.schema.json +239 -0
- package/scripts/diff_foundation_vs_project.sh +134 -0
- package/scripts/generate_docs.sh +200 -0
- package/scripts/init_project.sh +455 -0
- package/scripts/plan_upgrade.sh +268 -0
- package/scripts/upgrade_foundation.sh +365 -0
- package/scripts/validate-foundation.cjs +278 -0
- package/scripts/validate_foundation.sh +192 -0
- package/scripts/validate_project_setup.sh +171 -0
- package/tasks/README.md +94 -0
- package/tasks/_template/analysis.md +76 -0
- package/tasks/_template/design.md +121 -0
- package/tasks/_template/implementation.md +121 -0
- package/tasks/_template/release.md +119 -0
- package/tasks/_template/review.md +131 -0
- package/tasks/_template/subtasks/task.yaml +24 -0
- package/tasks/_template/task.yaml +75 -0
- package/tasks/_template/validation.md +128 -0
- package/templates/amazingteam.yml +81 -0
- package/templates/gitignore +14 -0
- package/templates/opencode.jsonc +216 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator Module Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
validateAgainstSchema,
|
|
7
|
+
validateConfig,
|
|
8
|
+
validateRequiredFiles,
|
|
9
|
+
validateProjectStructure
|
|
10
|
+
} = require('../lib/validator');
|
|
11
|
+
|
|
12
|
+
function testValidateAgainstSchema_TypeValidation() {
|
|
13
|
+
console.log('Testing validateAgainstSchema type validation...');
|
|
14
|
+
|
|
15
|
+
const schema = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
name: { type: 'string' },
|
|
19
|
+
count: { type: 'integer' },
|
|
20
|
+
active: { type: 'boolean' }
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const validValue = {
|
|
25
|
+
name: 'test',
|
|
26
|
+
count: 42,
|
|
27
|
+
active: true
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const result = validateAgainstSchema(validValue, schema);
|
|
31
|
+
console.assert(result.valid === true, 'Valid object should pass');
|
|
32
|
+
console.assert(result.errors.length === 0, 'Should have no errors');
|
|
33
|
+
|
|
34
|
+
const invalidValue = {
|
|
35
|
+
name: 123,
|
|
36
|
+
count: 'not a number',
|
|
37
|
+
active: 'yes'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const invalidResult = validateAgainstSchema(invalidValue, schema);
|
|
41
|
+
console.assert(invalidResult.valid === false, 'Invalid object should fail');
|
|
42
|
+
console.assert(invalidResult.errors.length > 0, 'Should have errors');
|
|
43
|
+
|
|
44
|
+
console.log(' ✓ type validation tests passed');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function testValidateAgainstSchema_EnumValidation() {
|
|
48
|
+
console.log('Testing validateAgainstSchema enum validation...');
|
|
49
|
+
|
|
50
|
+
const schema = {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
role: { enum: ['admin', 'user', 'guest'] },
|
|
54
|
+
status: { enum: ['active', 'inactive'] }
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const valid = { role: 'admin', status: 'active' };
|
|
59
|
+
const validResult = validateAgainstSchema(valid, schema);
|
|
60
|
+
console.assert(validResult.valid === true, 'Valid enum values should pass');
|
|
61
|
+
|
|
62
|
+
const invalid = { role: 'superuser', status: 'active' };
|
|
63
|
+
const invalidResult = validateAgainstSchema(invalid, schema);
|
|
64
|
+
console.assert(invalidResult.valid === false, 'Invalid enum should fail');
|
|
65
|
+
console.assert(invalidResult.errors[0].includes('must be one of'), 'Error should mention enum values');
|
|
66
|
+
|
|
67
|
+
console.log(' ✓ enum validation tests passed');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function testValidateAgainstSchema_StringValidation() {
|
|
71
|
+
console.log('Testing validateAgainstSchema string validation...');
|
|
72
|
+
|
|
73
|
+
const schema = {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
name: { type: 'string', minLength: 3, maxLength: 10 },
|
|
77
|
+
email: { type: 'string', pattern: '^[a-z]+@[a-z]+\\.[a-z]+$' }
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const validName = { name: 'test' };
|
|
82
|
+
const validNameResult = validateAgainstSchema(validName, schema);
|
|
83
|
+
console.assert(validNameResult.valid === true, 'Valid string length should pass');
|
|
84
|
+
|
|
85
|
+
const shortName = { name: 'ab' };
|
|
86
|
+
const shortResult = validateAgainstSchema(shortName, schema);
|
|
87
|
+
console.assert(shortResult.valid === false, 'Too short string should fail');
|
|
88
|
+
console.assert(shortResult.errors[0].includes('minLength'), 'Error should mention minLength');
|
|
89
|
+
|
|
90
|
+
const longName = { name: 'verylongname' };
|
|
91
|
+
const longResult = validateAgainstSchema(longName, schema);
|
|
92
|
+
console.assert(longResult.valid === false, 'Too long string should fail');
|
|
93
|
+
|
|
94
|
+
const validEmail = { email: 'test@example.com' };
|
|
95
|
+
const validEmailResult = validateAgainstSchema(validEmail, schema);
|
|
96
|
+
console.assert(validEmailResult.valid === true, 'Valid email pattern should pass');
|
|
97
|
+
|
|
98
|
+
const invalidEmail = { email: 'invalid-email' };
|
|
99
|
+
const invalidEmailResult = validateAgainstSchema(invalidEmail, schema);
|
|
100
|
+
console.assert(invalidEmailResult.valid === false, 'Invalid email pattern should fail');
|
|
101
|
+
|
|
102
|
+
console.log(' ✓ string validation tests passed');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function testValidateAgainstSchema_NumberValidation() {
|
|
106
|
+
console.log('Testing validateAgainstSchema number validation...');
|
|
107
|
+
|
|
108
|
+
const schema = {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
age: { type: 'integer', minimum: 0, maximum: 150 },
|
|
112
|
+
score: { type: 'number', minimum: 0, maximum: 100 }
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const valid = { age: 25, score: 85.5 };
|
|
117
|
+
const validResult = validateAgainstSchema(valid, schema);
|
|
118
|
+
console.assert(validResult.valid === true, 'Valid numbers should pass');
|
|
119
|
+
|
|
120
|
+
const negativeAge = { age: -5 };
|
|
121
|
+
const negativeResult = validateAgainstSchema(negativeAge, schema);
|
|
122
|
+
console.assert(negativeResult.valid === false, 'Negative age should fail');
|
|
123
|
+
console.assert(negativeResult.errors[0].includes('minimum'), 'Error should mention minimum');
|
|
124
|
+
|
|
125
|
+
const overMax = { score: 150 };
|
|
126
|
+
const overMaxResult = validateAgainstSchema(overMax, schema);
|
|
127
|
+
console.assert(overMaxResult.valid === false, 'Over maximum should fail');
|
|
128
|
+
|
|
129
|
+
const nonInteger = { age: 25.5 };
|
|
130
|
+
const nonIntResult = validateAgainstSchema(nonInteger, schema);
|
|
131
|
+
console.assert(nonIntResult.valid === false, 'Non-integer age should fail');
|
|
132
|
+
|
|
133
|
+
console.log(' ✓ number validation tests passed');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function testValidateAgainstSchema_ArrayValidation() {
|
|
137
|
+
console.log('Testing validateAgainstSchema array validation...');
|
|
138
|
+
|
|
139
|
+
const schema = {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
tags: {
|
|
143
|
+
type: 'array',
|
|
144
|
+
items: { type: 'string' },
|
|
145
|
+
minItems: 1,
|
|
146
|
+
maxItems: 5
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const valid = { tags: ['a', 'b', 'c'] };
|
|
152
|
+
const validResult = validateAgainstSchema(valid, schema);
|
|
153
|
+
console.assert(validResult.valid === true, 'Valid array should pass');
|
|
154
|
+
|
|
155
|
+
const empty = { tags: [] };
|
|
156
|
+
const emptyResult = validateAgainstSchema(empty, schema);
|
|
157
|
+
console.assert(emptyResult.valid === false, 'Empty array should fail (minItems)');
|
|
158
|
+
|
|
159
|
+
const tooMany = { tags: ['1', '2', '3', '4', '5', '6'] };
|
|
160
|
+
const tooManyResult = validateAgainstSchema(tooMany, schema);
|
|
161
|
+
console.assert(tooManyResult.valid === false, 'Array with too many items should fail');
|
|
162
|
+
|
|
163
|
+
const invalidItems = { tags: ['a', 123, 'b'] };
|
|
164
|
+
const invalidItemsResult = validateAgainstSchema(invalidItems, schema);
|
|
165
|
+
console.assert(invalidItemsResult.valid === false, 'Array with invalid items should fail');
|
|
166
|
+
|
|
167
|
+
console.log(' ✓ array validation tests passed');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function testValidateAgainstSchema_RequiredProperties() {
|
|
171
|
+
console.log('Testing validateAgainstSchema required properties...');
|
|
172
|
+
|
|
173
|
+
const schema = {
|
|
174
|
+
type: 'object',
|
|
175
|
+
required: ['name', 'version'],
|
|
176
|
+
properties: {
|
|
177
|
+
name: { type: 'string' },
|
|
178
|
+
version: { type: 'string' },
|
|
179
|
+
optional: { type: 'string' }
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const valid = { name: 'test', version: '1.0' };
|
|
184
|
+
const validResult = validateAgainstSchema(valid, schema);
|
|
185
|
+
console.assert(validResult.valid === true, 'Object with required properties should pass');
|
|
186
|
+
|
|
187
|
+
const missingVersion = { name: 'test' };
|
|
188
|
+
const missingResult = validateAgainstSchema(missingVersion, schema);
|
|
189
|
+
console.assert(missingResult.valid === false, 'Missing required property should fail');
|
|
190
|
+
console.assert(missingResult.errors[0].includes('required'), 'Error should mention required');
|
|
191
|
+
|
|
192
|
+
console.log(' ✓ required properties tests passed');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function testValidateAgainstSchema_NestedObjects() {
|
|
196
|
+
console.log('Testing validateAgainstSchema nested objects...');
|
|
197
|
+
|
|
198
|
+
const schema = {
|
|
199
|
+
type: 'object',
|
|
200
|
+
properties: {
|
|
201
|
+
project: {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: {
|
|
204
|
+
name: { type: 'string' },
|
|
205
|
+
language: { type: 'string' }
|
|
206
|
+
},
|
|
207
|
+
required: ['name']
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const valid = { project: { name: 'my-project', language: 'typescript' } };
|
|
213
|
+
const validResult = validateAgainstSchema(valid, schema);
|
|
214
|
+
console.assert(validResult.valid === true, 'Valid nested object should pass');
|
|
215
|
+
|
|
216
|
+
const missingNested = { project: { language: 'typescript' } };
|
|
217
|
+
const missingResult = validateAgainstSchema(missingNested, schema);
|
|
218
|
+
console.assert(missingResult.valid === false, 'Missing nested required property should fail');
|
|
219
|
+
|
|
220
|
+
console.log(' ✓ nested objects tests passed');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function testValidateConfig() {
|
|
224
|
+
console.log('Testing validateConfig...');
|
|
225
|
+
|
|
226
|
+
const schema = {
|
|
227
|
+
type: 'object',
|
|
228
|
+
required: ['version', 'project'],
|
|
229
|
+
properties: {
|
|
230
|
+
version: { type: 'string' },
|
|
231
|
+
project: {
|
|
232
|
+
type: 'object',
|
|
233
|
+
required: ['name'],
|
|
234
|
+
properties: {
|
|
235
|
+
name: { type: 'string' },
|
|
236
|
+
language: { type: 'string' }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const validConfig = {
|
|
243
|
+
version: '1.0',
|
|
244
|
+
project: {
|
|
245
|
+
name: 'test-project',
|
|
246
|
+
language: 'typescript'
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const result = validateConfig(validConfig, schema);
|
|
251
|
+
console.assert(result.valid === true, 'Valid config should pass');
|
|
252
|
+
|
|
253
|
+
const invalidConfig = {
|
|
254
|
+
version: '1.0'
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const invalidResult = validateConfig(invalidConfig, schema);
|
|
258
|
+
console.assert(invalidResult.valid === false, 'Invalid config should fail');
|
|
259
|
+
|
|
260
|
+
console.log(' ✓ validateConfig tests passed');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function testValidateRequiredFiles() {
|
|
264
|
+
console.log('Testing validateRequiredFiles...');
|
|
265
|
+
|
|
266
|
+
const result = validateRequiredFiles(process.cwd(), ['package.json']);
|
|
267
|
+
console.assert(result.valid === true, 'package.json should exist');
|
|
268
|
+
console.assert(result.missing.length === 0, 'Should have no missing files');
|
|
269
|
+
|
|
270
|
+
const missingResult = validateRequiredFiles(process.cwd(), ['nonexistent-file.xyz']);
|
|
271
|
+
console.assert(missingResult.valid === false, 'Non-existent file should fail');
|
|
272
|
+
console.assert(missingResult.missing.length === 1, 'Should have one missing file');
|
|
273
|
+
|
|
274
|
+
console.log(' ✓ validateRequiredFiles tests passed');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function testValidateProjectStructure() {
|
|
278
|
+
console.log('Testing validateProjectStructure...');
|
|
279
|
+
|
|
280
|
+
const result = validateProjectStructure(process.cwd());
|
|
281
|
+
|
|
282
|
+
console.assert(Array.isArray(result.issues), 'Should return issues array');
|
|
283
|
+
|
|
284
|
+
if (result.valid) {
|
|
285
|
+
console.log(' Project structure is valid');
|
|
286
|
+
} else {
|
|
287
|
+
console.log(` Project has issues: ${result.issues.join(', ')}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.log(' ✓ validateProjectStructure tests passed');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function runAll() {
|
|
294
|
+
console.log('\n=== Validator Module Tests ===\n');
|
|
295
|
+
|
|
296
|
+
testValidateAgainstSchema_TypeValidation();
|
|
297
|
+
testValidateAgainstSchema_EnumValidation();
|
|
298
|
+
testValidateAgainstSchema_StringValidation();
|
|
299
|
+
testValidateAgainstSchema_NumberValidation();
|
|
300
|
+
testValidateAgainstSchema_ArrayValidation();
|
|
301
|
+
testValidateAgainstSchema_RequiredProperties();
|
|
302
|
+
testValidateAgainstSchema_NestedObjects();
|
|
303
|
+
testValidateConfig();
|
|
304
|
+
testValidateRequiredFiles();
|
|
305
|
+
testValidateProjectStructure();
|
|
306
|
+
|
|
307
|
+
console.log('\n✅ All validator tests passed!\n');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
runAll();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AmazingTeam Foundation GitHub Action
|
|
2
|
+
# Downloads and sets up AmazingTeam Foundation for use in workflows
|
|
3
|
+
|
|
4
|
+
name: 'AmazingTeam Setup'
|
|
5
|
+
description: 'Set up AmazingTeam Foundation for AI-powered development'
|
|
6
|
+
author: 'AmazingTeam'
|
|
7
|
+
branding:
|
|
8
|
+
icon: 'cpu'
|
|
9
|
+
color: 'blue'
|
|
10
|
+
|
|
11
|
+
inputs:
|
|
12
|
+
version:
|
|
13
|
+
description: 'AmazingTeam Foundation version to use'
|
|
14
|
+
required: false
|
|
15
|
+
default: '3.0.0'
|
|
16
|
+
|
|
17
|
+
config:
|
|
18
|
+
description: 'Path to amazingteam.config.yaml'
|
|
19
|
+
required: false
|
|
20
|
+
default: 'amazingteam.config.yaml'
|
|
21
|
+
|
|
22
|
+
overlay:
|
|
23
|
+
description: 'Technology overlay to apply (e.g., python-backend, web-fullstack)'
|
|
24
|
+
required: false
|
|
25
|
+
default: ''
|
|
26
|
+
|
|
27
|
+
cache:
|
|
28
|
+
description: 'Enable caching of foundation downloads'
|
|
29
|
+
required: false
|
|
30
|
+
default: 'true'
|
|
31
|
+
|
|
32
|
+
cache-dir:
|
|
33
|
+
description: 'Directory to cache foundation downloads'
|
|
34
|
+
required: false
|
|
35
|
+
default: '~/.amazingteam-cache'
|
|
36
|
+
|
|
37
|
+
outputs:
|
|
38
|
+
foundation-path:
|
|
39
|
+
description: 'Path to the downloaded foundation'
|
|
40
|
+
value: ${{ steps.setup.outputs.foundation-path }}
|
|
41
|
+
|
|
42
|
+
setup-complete:
|
|
43
|
+
description: 'Whether setup completed successfully'
|
|
44
|
+
value: ${{ steps.setup.outputs.success }}
|
|
45
|
+
|
|
46
|
+
version:
|
|
47
|
+
description: 'Foundation version that was set up'
|
|
48
|
+
value: ${{ steps.setup.outputs.version }}
|
|
49
|
+
|
|
50
|
+
runs:
|
|
51
|
+
using: 'node20'
|
|
52
|
+
main: 'index.js'
|
|
53
|
+
|
|
54
|
+
# Future: Support composite action for more complex setup
|
|
55
|
+
# runs:
|
|
56
|
+
# using: 'composite'
|
|
57
|
+
# steps:
|
|
58
|
+
# - name: Setup AmazingTeam
|
|
59
|
+
# shell: bash
|
|
60
|
+
# run: |
|
|
61
|
+
# node ${{ github.action_path }}/index.js
|
package/action/index.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AmazingTeam Foundation GitHub Action
|
|
3
|
+
* Main entry point for the action
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const core = require('@actions/core');
|
|
7
|
+
const github = require('@actions/github');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const downloader = require('./lib/downloader');
|
|
12
|
+
const merger = require('./lib/merger');
|
|
13
|
+
const setup = require('./lib/setup');
|
|
14
|
+
const validator = require('./lib/validator');
|
|
15
|
+
const PathResolver = require('./lib/path-resolver');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse YAML config (minimal implementation)
|
|
19
|
+
* For production, use js-yaml package
|
|
20
|
+
*/
|
|
21
|
+
function parseYamlConfig(content) {
|
|
22
|
+
const lines = content.split('\n');
|
|
23
|
+
const result = {};
|
|
24
|
+
let currentPath = [];
|
|
25
|
+
let currentIndent = 0;
|
|
26
|
+
|
|
27
|
+
function setValue(obj, keys, value) {
|
|
28
|
+
let current = obj;
|
|
29
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
30
|
+
if (!current[keys[i]]) {
|
|
31
|
+
current[keys[i]] = {};
|
|
32
|
+
}
|
|
33
|
+
current = current[keys[i]];
|
|
34
|
+
}
|
|
35
|
+
current[keys[keys.length - 1]] = value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
if (line.trim().startsWith('#') || line.trim() === '') continue;
|
|
40
|
+
|
|
41
|
+
const indent = line.search(/\S/);
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
const colonIndex = trimmed.indexOf(':');
|
|
44
|
+
|
|
45
|
+
if (colonIndex > 0) {
|
|
46
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
47
|
+
let value = trimmed.substring(colonIndex + 1).trim();
|
|
48
|
+
|
|
49
|
+
// Adjust path based on indent
|
|
50
|
+
const level = Math.floor(indent / 2);
|
|
51
|
+
currentPath = currentPath.slice(0, level);
|
|
52
|
+
currentPath.push(key);
|
|
53
|
+
|
|
54
|
+
if (value !== '') {
|
|
55
|
+
// Parse value
|
|
56
|
+
let parsedValue;
|
|
57
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
58
|
+
parsedValue = value.slice(1, -1).split(',').map(s => s.trim().replace(/^["']|["']$/g, ''));
|
|
59
|
+
} else if (value === 'true') {
|
|
60
|
+
parsedValue = true;
|
|
61
|
+
} else if (value === 'false') {
|
|
62
|
+
parsedValue = false;
|
|
63
|
+
} else if (value === 'null') {
|
|
64
|
+
parsedValue = null;
|
|
65
|
+
} else if (!isNaN(Number(value))) {
|
|
66
|
+
parsedValue = Number(value);
|
|
67
|
+
} else {
|
|
68
|
+
parsedValue = value.replace(/^["']|["']$/g, '');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setValue(result, currentPath, parsedValue);
|
|
72
|
+
currentPath.pop();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Load user configuration
|
|
82
|
+
*/
|
|
83
|
+
async function loadUserConfig(configPath, projectDir) {
|
|
84
|
+
const fullPath = path.join(projectDir, configPath);
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(fullPath)) {
|
|
87
|
+
core.warning(`Config file not found: ${configPath}, using defaults`);
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
92
|
+
return parseYamlConfig(content);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Load preset configuration
|
|
97
|
+
*/
|
|
98
|
+
async function loadPreset(presetName, foundationDir) {
|
|
99
|
+
if (!presetName) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const presetPath = path.join(foundationDir, 'presets', `${presetName}.yaml`);
|
|
104
|
+
|
|
105
|
+
if (!fs.existsSync(presetPath)) {
|
|
106
|
+
core.warning(`Preset not found: ${presetName}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const content = fs.readFileSync(presetPath, 'utf-8');
|
|
111
|
+
return parseYamlConfig(content);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Load default configuration
|
|
116
|
+
*/
|
|
117
|
+
async function loadDefaultConfig(foundationDir) {
|
|
118
|
+
const defaultPath = path.join(foundationDir, 'presets', 'default.yaml');
|
|
119
|
+
|
|
120
|
+
if (!fs.existsSync(defaultPath)) {
|
|
121
|
+
core.warning('Default preset not found, using minimal config');
|
|
122
|
+
return {
|
|
123
|
+
version: '1.0',
|
|
124
|
+
project: {
|
|
125
|
+
name: 'unknown',
|
|
126
|
+
language: 'typescript'
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const content = fs.readFileSync(defaultPath, 'utf-8');
|
|
132
|
+
return parseYamlConfig(content);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Main action function
|
|
137
|
+
*/
|
|
138
|
+
async function run() {
|
|
139
|
+
try {
|
|
140
|
+
// Get inputs
|
|
141
|
+
const version = core.getInput('version') || '3.0.0';
|
|
142
|
+
const configPath = core.getInput('config') || 'amazingteam.config.yaml';
|
|
143
|
+
const overlay = core.getInput('overlay') || '';
|
|
144
|
+
const useCache = core.getInput('cache') !== 'false';
|
|
145
|
+
const cacheDir = core.getInput('cache-dir') || path.join(process.env.HOME || '', '.amazingteam-cache');
|
|
146
|
+
|
|
147
|
+
const projectDir = process.env.GITHUB_WORKSPACE || process.cwd();
|
|
148
|
+
|
|
149
|
+
core.info(`Setting up AmazingTeam Foundation v${version}`);
|
|
150
|
+
core.info(`Project directory: ${projectDir}`);
|
|
151
|
+
|
|
152
|
+
// Step 1: Download foundation
|
|
153
|
+
core.info('Downloading foundation...');
|
|
154
|
+
const foundationDir = await downloader.downloadFoundation({
|
|
155
|
+
version,
|
|
156
|
+
cacheDir: useCache ? cacheDir : null
|
|
157
|
+
});
|
|
158
|
+
core.info(`Foundation downloaded to: ${foundationDir}`);
|
|
159
|
+
|
|
160
|
+
// Step 2: Load configurations
|
|
161
|
+
core.info('Loading configurations...');
|
|
162
|
+
const defaultConfig = await loadDefaultConfig(foundationDir);
|
|
163
|
+
const presetConfig = overlay ? await loadPreset(overlay, foundationDir) : null;
|
|
164
|
+
const userConfig = await loadUserConfig(configPath, projectDir);
|
|
165
|
+
|
|
166
|
+
// Step 3: Merge configurations
|
|
167
|
+
core.info('Merging configurations...');
|
|
168
|
+
const mergedConfig = merger.mergeWithPreset(presetConfig, defaultConfig, userConfig);
|
|
169
|
+
|
|
170
|
+
// Validate merged config
|
|
171
|
+
const validation = merger.validateMergedConfig(mergedConfig);
|
|
172
|
+
if (!validation.valid) {
|
|
173
|
+
core.warning(`Configuration validation warnings: ${validation.errors.join(', ')}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Step 4: Setup runtime
|
|
177
|
+
core.info('Setting up runtime...');
|
|
178
|
+
const setupResult = setup.setup(foundationDir, projectDir, mergedConfig);
|
|
179
|
+
|
|
180
|
+
// Step 5: Verify setup
|
|
181
|
+
const verification = setup.verifySetup(projectDir);
|
|
182
|
+
if (!verification.valid) {
|
|
183
|
+
core.warning(`Setup verification issues: ${verification.issues.join(', ')}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Set outputs
|
|
187
|
+
core.setOutput('foundation-path', foundationDir);
|
|
188
|
+
core.setOutput('setup-complete', 'true');
|
|
189
|
+
core.setOutput('version', version);
|
|
190
|
+
|
|
191
|
+
// Export environment variables for subsequent steps
|
|
192
|
+
core.exportVariable('AI_TEAM_FOUNDATION_PATH', foundationDir);
|
|
193
|
+
core.exportVariable('AI_TEAM_VERSION', version);
|
|
194
|
+
|
|
195
|
+
core.info('AmazingTeam Foundation setup complete!');
|
|
196
|
+
|
|
197
|
+
// Summary
|
|
198
|
+
core.summary.addRaw(`
|
|
199
|
+
## AmazingTeam Foundation Setup Complete
|
|
200
|
+
|
|
201
|
+
| Property | Value |
|
|
202
|
+
|----------|-------|
|
|
203
|
+
| Version | ${version} |
|
|
204
|
+
| Foundation Path | ${foundationDir} |
|
|
205
|
+
| Config | ${configPath} |
|
|
206
|
+
| Overlay | ${overlay || 'none'} |
|
|
207
|
+
|
|
208
|
+
### Created Directories
|
|
209
|
+
${setupResult.createdDirectories.map(d => `- ${d}`).join('\n')}
|
|
210
|
+
|
|
211
|
+
### Generated Files
|
|
212
|
+
- ${setupResult.opencodePath}
|
|
213
|
+
`).write();
|
|
214
|
+
|
|
215
|
+
} catch (error) {
|
|
216
|
+
core.setFailed(`AmazingTeam setup failed: ${error.message}`);
|
|
217
|
+
core.error(error.stack);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Run the action
|
|
223
|
+
run();
|