@atlashub/smartstack-cli 1.36.0 → 2.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.
Files changed (103) hide show
  1. package/config/mcp-defaults.json +62 -0
  2. package/dist/index.js +57 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/mcp-entry.mjs +16984 -0
  5. package/dist/mcp-entry.mjs.map +1 -0
  6. package/package.json +14 -5
  7. package/templates/agents/gitflow/start.md +5 -4
  8. package/templates/agents/mcp-healthcheck.md +15 -13
  9. package/templates/mcp-scaffolding/component.tsx.hbs +298 -0
  10. package/templates/mcp-scaffolding/controller.cs.hbs +184 -0
  11. package/templates/mcp-scaffolding/entity-extension.cs.hbs +231 -0
  12. package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +116 -0
  13. package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +133 -0
  14. package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +134 -0
  15. package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +261 -0
  16. package/templates/mcp-scaffolding/service-extension.cs.hbs +53 -0
  17. package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +413 -0
  18. package/templates/mcp-scaffolding/tests/entity.test.cs.hbs +239 -0
  19. package/templates/mcp-scaffolding/tests/repository.test.cs.hbs +441 -0
  20. package/templates/mcp-scaffolding/tests/security.test.cs.hbs +442 -0
  21. package/templates/mcp-scaffolding/tests/service.test.cs.hbs +390 -0
  22. package/templates/mcp-scaffolding/tests/validator.test.cs.hbs +428 -0
  23. package/templates/ralph/README.md +3 -3
  24. package/templates/ralph/ralph.config.yaml +2 -2
  25. package/templates/skills/admin/SKILL.md +42 -0
  26. package/templates/skills/application/steps/step-01-navigation.md +226 -43
  27. package/templates/skills/application/steps/step-03-roles.md +160 -38
  28. package/templates/skills/application/steps/step-04-backend.md +109 -2
  29. package/templates/skills/application/templates-seed.md +200 -1
  30. package/templates/skills/business-analyse/_shared.md +24 -1
  31. package/templates/skills/business-analyse/questionnaire/01-context.md +4 -4
  32. package/templates/skills/business-analyse/questionnaire/02-stakeholders.md +3 -3
  33. package/templates/skills/business-analyse/questionnaire/03-scope.md +4 -4
  34. package/templates/skills/business-analyse/questionnaire/04-data.md +7 -7
  35. package/templates/skills/business-analyse/questionnaire/05-integrations.md +1 -1
  36. package/templates/skills/business-analyse/questionnaire/06-security.md +3 -3
  37. package/templates/skills/business-analyse/questionnaire/07-ui.md +1 -1
  38. package/templates/skills/business-analyse/questionnaire/08-performance.md +3 -3
  39. package/templates/skills/business-analyse/questionnaire/09-constraints.md +4 -4
  40. package/templates/skills/business-analyse/questionnaire/10-documentation.md +2 -2
  41. package/templates/skills/business-analyse/questionnaire/11-data-lifecycle.md +2 -2
  42. package/templates/skills/business-analyse/questionnaire/12-migration.md +1 -1
  43. package/templates/skills/business-analyse/questionnaire/13-cross-module.md +2 -2
  44. package/templates/skills/business-analyse/steps/step-01-discover.md +50 -25
  45. package/templates/skills/business-analyse/steps/step-05-handoff.md +133 -34
  46. package/templates/skills/cc-agent/SKILL.md +129 -0
  47. package/templates/skills/cc-agent/references/agent-frontmatter.md +213 -0
  48. package/templates/skills/cc-agent/references/permission-modes.md +102 -0
  49. package/templates/skills/cc-agent/references/tools-reference.md +144 -0
  50. package/templates/skills/cc-agent/steps/step-00-init.md +134 -0
  51. package/templates/skills/cc-agent/steps/step-01-design.md +186 -0
  52. package/templates/skills/cc-agent/steps/step-02-generate.md +204 -0
  53. package/templates/skills/cc-agent/steps/step-03-validate.md +130 -0
  54. package/templates/skills/cc-agent/templates/agent-categorized.md +67 -0
  55. package/templates/skills/cc-agent/templates/agent-standalone.md +56 -0
  56. package/templates/skills/cc-agent/templates/agent-with-skills.md +94 -0
  57. package/templates/skills/cc-audit/SKILL.md +108 -0
  58. package/templates/skills/cc-audit/references/agent-checklist.md +91 -0
  59. package/templates/skills/cc-audit/references/hook-checklist.md +110 -0
  60. package/templates/skills/cc-audit/references/skill-checklist.md +70 -0
  61. package/templates/skills/cc-audit/steps/step-00-init.md +98 -0
  62. package/templates/skills/cc-audit/steps/step-01-scan.md +142 -0
  63. package/templates/skills/cc-audit/steps/step-02-analyze.md +158 -0
  64. package/templates/skills/cc-audit/steps/step-03-report.md +142 -0
  65. package/templates/skills/cc-skill/SKILL.md +134 -0
  66. package/templates/skills/cc-skill/references/best-practices.md +167 -0
  67. package/templates/skills/cc-skill/references/frontmatter-reference.md +182 -0
  68. package/templates/skills/cc-skill/references/skill-patterns.md +199 -0
  69. package/templates/skills/cc-skill/steps/step-00-init.md +119 -0
  70. package/templates/skills/cc-skill/steps/step-01-design.md +199 -0
  71. package/templates/skills/cc-skill/steps/step-02-generate.md +145 -0
  72. package/templates/skills/cc-skill/steps/step-03-steps.md +151 -0
  73. package/templates/skills/cc-skill/steps/step-04-validate.md +124 -0
  74. package/templates/skills/cc-skill/templates/skill-forked.md +85 -0
  75. package/templates/skills/cc-skill/templates/skill-progressive.md +102 -0
  76. package/templates/skills/cc-skill/templates/skill-simple.md +75 -0
  77. package/templates/skills/cc-skill/templates/step-template.md +82 -0
  78. package/templates/skills/check-version/SKILL.md +6 -0
  79. package/templates/skills/debug/SKILL.md +4 -0
  80. package/templates/skills/documentation/SKILL.md +1 -0
  81. package/templates/skills/efcore/SKILL.md +5 -0
  82. package/templates/skills/efcore/steps/db/step-deploy.md +26 -5
  83. package/templates/skills/efcore/steps/shared/step-00-init.md +21 -7
  84. package/templates/skills/explore/SKILL.md +28 -32
  85. package/templates/skills/feature-full/SKILL.md +1 -0
  86. package/templates/skills/gitflow/SKILL.md +8 -0
  87. package/templates/skills/gitflow/steps/step-start.md +45 -10
  88. package/templates/skills/mcp/SKILL.md +38 -18
  89. package/templates/skills/quick-search/SKILL.md +8 -1
  90. package/templates/skills/ralph-loop/SKILL.md +1 -1
  91. package/templates/skills/ralph-loop/steps/step-00-init.md +8 -68
  92. package/templates/skills/ralph-loop/steps/step-04-check.md +1 -1
  93. package/templates/skills/refactor/SKILL.md +1 -0
  94. package/templates/skills/review-code/SKILL.md +7 -1
  95. package/templates/skills/ui-components/SKILL.md +31 -438
  96. package/templates/skills/ui-components/accessibility.md +170 -0
  97. package/templates/skills/ui-components/patterns/data-table.md +39 -0
  98. package/templates/skills/ui-components/patterns/entity-card.md +77 -0
  99. package/templates/skills/ui-components/patterns/grid-layout.md +91 -0
  100. package/templates/skills/ui-components/patterns/kanban.md +43 -0
  101. package/templates/skills/ui-components/style-guide.md +86 -0
  102. package/templates/skills/utils/SKILL.md +1 -0
  103. package/templates/skills/validate/SKILL.md +1 -0
@@ -0,0 +1,390 @@
1
+ {{!-- SmartStack Service Test Template --}}
2
+ {{!-- Generates unit tests for application services following SmartStack conventions --}}
3
+
4
+ using FluentAssertions;
5
+ using Moq;
6
+ using Xunit;
7
+ using {{namespace}}.Application.Services;
8
+ using {{namespace}}.Application.Interfaces;
9
+ using {{namespace}}.Domain.Entities;
10
+ using {{namespace}}.Domain.Interfaces;
11
+
12
+ namespace {{namespace}}.Tests.Unit.Services;
13
+
14
+ /// <summary>
15
+ /// Unit tests for <see cref="{{name}}Service"/>.
16
+ /// Follows SmartStack testing conventions: {Method}_When{Condition}_Should{Result}
17
+ /// </summary>
18
+ public class {{name}}ServiceTests
19
+ {
20
+ private readonly Mock<I{{name}}Repository> _mockRepository;
21
+ private readonly Mock<IUnitOfWork> _mockUnitOfWork;
22
+ {{#unless isSystemEntity}}
23
+ private readonly Mock<ITenantContext> _mockTenantContext;
24
+ {{/unless}}
25
+ private readonly {{name}}Service _sut;
26
+
27
+ public {{name}}ServiceTests()
28
+ {
29
+ _mockRepository = new Mock<I{{name}}Repository>();
30
+ _mockUnitOfWork = new Mock<IUnitOfWork>();
31
+ {{#unless isSystemEntity}}
32
+ _mockTenantContext = new Mock<ITenantContext>();
33
+ _mockTenantContext.Setup(x => x.TenantId).Returns(Guid.NewGuid());
34
+ {{/unless}}
35
+
36
+ _sut = new {{name}}Service(
37
+ _mockRepository.Object,
38
+ _mockUnitOfWork.Object{{#unless isSystemEntity}},
39
+ _mockTenantContext.Object{{/unless}}
40
+ );
41
+ }
42
+
43
+ #region GetById Tests
44
+
45
+ [Fact]
46
+ public async Task GetByIdAsync_WhenExists_ShouldReturnEntity()
47
+ {
48
+ // Arrange
49
+ var id = Guid.NewGuid();
50
+ var entity = CreateValid{{name}}(id);
51
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
52
+ .ReturnsAsync(entity);
53
+
54
+ // Act
55
+ var result = await _sut.GetByIdAsync(id);
56
+
57
+ // Assert
58
+ result.Should().NotBeNull();
59
+ result!.Id.Should().Be(id);
60
+ }
61
+
62
+ [Fact]
63
+ public async Task GetByIdAsync_WhenNotExists_ShouldReturnNull()
64
+ {
65
+ // Arrange
66
+ var id = Guid.NewGuid();
67
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
68
+ .ReturnsAsync(({{name}}?)null);
69
+
70
+ // Act
71
+ var result = await _sut.GetByIdAsync(id);
72
+
73
+ // Assert
74
+ result.Should().BeNull();
75
+ }
76
+
77
+ #endregion
78
+
79
+ #region GetAll Tests
80
+
81
+ [Fact]
82
+ public async Task GetAllAsync_WhenEntitiesExist_ShouldReturnAll()
83
+ {
84
+ // Arrange
85
+ var entities = new List<{{name}}>
86
+ {
87
+ CreateValid{{name}}(),
88
+ CreateValid{{name}}()
89
+ };
90
+ _mockRepository.Setup(x => x.GetAllAsync(It.IsAny<CancellationToken>()))
91
+ .ReturnsAsync(entities);
92
+
93
+ // Act
94
+ var result = await _sut.GetAllAsync();
95
+
96
+ // Assert
97
+ result.Should().HaveCount(2);
98
+ }
99
+
100
+ [Fact]
101
+ public async Task GetAllAsync_WhenNoEntities_ShouldReturnEmptyList()
102
+ {
103
+ // Arrange
104
+ _mockRepository.Setup(x => x.GetAllAsync(It.IsAny<CancellationToken>()))
105
+ .ReturnsAsync(new List<{{name}}>());
106
+
107
+ // Act
108
+ var result = await _sut.GetAllAsync();
109
+
110
+ // Assert
111
+ result.Should().BeEmpty();
112
+ }
113
+
114
+ #endregion
115
+
116
+ #region Create Tests
117
+
118
+ [Fact]
119
+ public async Task CreateAsync_WhenValidData_ShouldCreateAndReturnEntity()
120
+ {
121
+ // Arrange
122
+ var request = new Create{{name}}Request
123
+ {
124
+ Code = "NEW-001",
125
+ // Add other required properties
126
+ };
127
+
128
+ _mockRepository.Setup(x => x.AddAsync(It.IsAny<{{name}}>(), It.IsAny<CancellationToken>()))
129
+ .Returns(Task.CompletedTask);
130
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
131
+ .ReturnsAsync(1);
132
+
133
+ // Act
134
+ var result = await _sut.CreateAsync(request);
135
+
136
+ // Assert
137
+ result.Should().NotBeNull();
138
+ result.Code.Should().Be(request.Code);
139
+ _mockRepository.Verify(x => x.AddAsync(It.IsAny<{{name}}>(), It.IsAny<CancellationToken>()), Times.Once);
140
+ _mockUnitOfWork.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
141
+ }
142
+
143
+ [Fact]
144
+ public async Task CreateAsync_WhenDuplicateCode_ShouldThrowException()
145
+ {
146
+ // Arrange
147
+ var request = new Create{{name}}Request { Code = "EXISTING-001" };
148
+ _mockRepository.Setup(x => x.ExistsByCodeAsync(request.Code, It.IsAny<CancellationToken>()))
149
+ .ReturnsAsync(true);
150
+
151
+ // Act
152
+ var act = () => _sut.CreateAsync(request);
153
+
154
+ // Assert
155
+ await act.Should().ThrowAsync<BusinessException>()
156
+ .WithMessage("*already exists*");
157
+ }
158
+
159
+ #endregion
160
+
161
+ #region Update Tests
162
+
163
+ [Fact]
164
+ public async Task UpdateAsync_WhenExists_ShouldUpdateEntity()
165
+ {
166
+ // Arrange
167
+ var id = Guid.NewGuid();
168
+ var entity = CreateValid{{name}}(id);
169
+ var request = new Update{{name}}Request
170
+ {
171
+ // Add properties to update
172
+ };
173
+
174
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
175
+ .ReturnsAsync(entity);
176
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
177
+ .ReturnsAsync(1);
178
+
179
+ // Act
180
+ var result = await _sut.UpdateAsync(id, request);
181
+
182
+ // Assert
183
+ result.Should().NotBeNull();
184
+ _mockUnitOfWork.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
185
+ }
186
+
187
+ [Fact]
188
+ public async Task UpdateAsync_WhenNotExists_ShouldThrowException()
189
+ {
190
+ // Arrange
191
+ var id = Guid.NewGuid();
192
+ var request = new Update{{name}}Request();
193
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
194
+ .ReturnsAsync(({{name}}?)null);
195
+
196
+ // Act
197
+ var act = () => _sut.UpdateAsync(id, request);
198
+
199
+ // Assert
200
+ await act.Should().ThrowAsync<NotFoundException>();
201
+ }
202
+
203
+ #endregion
204
+
205
+ #region Delete Tests
206
+
207
+ {{#if includeSoftDelete}}
208
+ [Fact]
209
+ public async Task DeleteAsync_WhenExists_ShouldSoftDelete()
210
+ {
211
+ // Arrange
212
+ var id = Guid.NewGuid();
213
+ var entity = CreateValid{{name}}(id);
214
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
215
+ .ReturnsAsync(entity);
216
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
217
+ .ReturnsAsync(1);
218
+
219
+ // Act
220
+ await _sut.DeleteAsync(id);
221
+
222
+ // Assert
223
+ entity.IsDeleted.Should().BeTrue();
224
+ _mockUnitOfWork.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
225
+ }
226
+
227
+ [Fact]
228
+ public async Task DeleteAsync_WhenAlreadyDeleted_ShouldThrowException()
229
+ {
230
+ // Arrange
231
+ var id = Guid.NewGuid();
232
+ var entity = CreateValid{{name}}(id);
233
+ entity.SoftDelete("previous@example.com");
234
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
235
+ .ReturnsAsync(entity);
236
+
237
+ // Act
238
+ var act = () => _sut.DeleteAsync(id);
239
+
240
+ // Assert
241
+ await act.Should().ThrowAsync<BusinessException>()
242
+ .WithMessage("*already deleted*");
243
+ }
244
+ {{else}}
245
+ [Fact]
246
+ public async Task DeleteAsync_WhenExists_ShouldDelete()
247
+ {
248
+ // Arrange
249
+ var id = Guid.NewGuid();
250
+ var entity = CreateValid{{name}}(id);
251
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
252
+ .ReturnsAsync(entity);
253
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
254
+ .ReturnsAsync(1);
255
+
256
+ // Act
257
+ await _sut.DeleteAsync(id);
258
+
259
+ // Assert
260
+ _mockRepository.Verify(x => x.Remove(entity), Times.Once);
261
+ _mockUnitOfWork.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
262
+ }
263
+ {{/if}}
264
+
265
+ [Fact]
266
+ public async Task DeleteAsync_WhenNotExists_ShouldThrowException()
267
+ {
268
+ // Arrange
269
+ var id = Guid.NewGuid();
270
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
271
+ .ReturnsAsync(({{name}}?)null);
272
+
273
+ // Act
274
+ var act = () => _sut.DeleteAsync(id);
275
+
276
+ // Assert
277
+ await act.Should().ThrowAsync<NotFoundException>();
278
+ }
279
+
280
+ #endregion
281
+
282
+ {{#unless isSystemEntity}}
283
+ #region Tenant Isolation Tests
284
+
285
+ [Fact]
286
+ public async Task CreateAsync_ShouldUseCurrentTenantId()
287
+ {
288
+ // Arrange
289
+ var expectedTenantId = Guid.NewGuid();
290
+ _mockTenantContext.Setup(x => x.TenantId).Returns(expectedTenantId);
291
+
292
+ var request = new Create{{name}}Request { Code = "TEST" };
293
+ {{name}}? capturedEntity = null;
294
+ _mockRepository.Setup(x => x.AddAsync(It.IsAny<{{name}}>(), It.IsAny<CancellationToken>()))
295
+ .Callback<{{name}}, CancellationToken>((e, _) => capturedEntity = e)
296
+ .Returns(Task.CompletedTask);
297
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
298
+ .ReturnsAsync(1);
299
+
300
+ // Act
301
+ await _sut.CreateAsync(request);
302
+
303
+ // Assert
304
+ capturedEntity.Should().NotBeNull();
305
+ capturedEntity!.TenantId.Should().Be(expectedTenantId);
306
+ }
307
+
308
+ [Fact]
309
+ public async Task GetByIdAsync_ShouldRespectTenantScope()
310
+ {
311
+ // Arrange
312
+ var id = Guid.NewGuid();
313
+ var tenantId = Guid.NewGuid();
314
+ _mockTenantContext.Setup(x => x.TenantId).Returns(tenantId);
315
+
316
+ // Act
317
+ await _sut.GetByIdAsync(id);
318
+
319
+ // Assert
320
+ _mockRepository.Verify(
321
+ x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()),
322
+ Times.Once
323
+ );
324
+ // Repository should be configured to filter by tenant
325
+ }
326
+
327
+ #endregion
328
+ {{/unless}}
329
+
330
+ #region Error Handling Tests
331
+
332
+ [Fact]
333
+ public async Task CreateAsync_WhenRepositoryFails_ShouldThrowException()
334
+ {
335
+ // Arrange
336
+ var request = new Create{{name}}Request { Code = "TEST" };
337
+ _mockRepository.Setup(x => x.AddAsync(It.IsAny<{{name}}>(), It.IsAny<CancellationToken>()))
338
+ .ThrowsAsync(new Exception("Database error"));
339
+
340
+ // Act
341
+ var act = () => _sut.CreateAsync(request);
342
+
343
+ // Assert
344
+ await act.Should().ThrowAsync<Exception>()
345
+ .WithMessage("*Database error*");
346
+ }
347
+
348
+ [Fact]
349
+ public async Task UpdateAsync_WhenConcurrencyConflict_ShouldThrowException()
350
+ {
351
+ // Arrange
352
+ var id = Guid.NewGuid();
353
+ var entity = CreateValid{{name}}(id);
354
+ var request = new Update{{name}}Request();
355
+
356
+ _mockRepository.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
357
+ .ReturnsAsync(entity);
358
+ _mockUnitOfWork.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
359
+ .ThrowsAsync(new DbUpdateConcurrencyException());
360
+
361
+ // Act
362
+ var act = () => _sut.UpdateAsync(id, request);
363
+
364
+ // Assert
365
+ await act.Should().ThrowAsync<ConcurrencyException>();
366
+ }
367
+
368
+ #endregion
369
+
370
+ #region Helper Methods
371
+
372
+ private static {{name}} CreateValid{{name}}(Guid? id = null)
373
+ {
374
+ {{#if isSystemEntity}}
375
+ var entity = {{name}}.Create("TEST-001", "test@example.com");
376
+ {{else}}
377
+ var entity = {{name}}.Create(Guid.NewGuid(), "TEST-001", "test@example.com");
378
+ {{/if}}
379
+
380
+ if (id.HasValue)
381
+ {
382
+ // Use reflection to set Id for testing
383
+ typeof({{name}}).GetProperty("Id")!.SetValue(entity, id.Value);
384
+ }
385
+
386
+ return entity;
387
+ }
388
+
389
+ #endregion
390
+ }