@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,441 @@
1
+ {{!-- SmartStack Repository Test Template --}}
2
+ {{!-- Generates integration tests for repositories following SmartStack conventions --}}
3
+
4
+ using FluentAssertions;
5
+ using Microsoft.EntityFrameworkCore;
6
+ using Xunit;
7
+ using {{namespace}}.Domain.Entities;
8
+ using {{namespace}}.Infrastructure.Data;
9
+ using {{namespace}}.Infrastructure.Repositories;
10
+
11
+ namespace {{namespace}}.Tests.Integration.Repositories;
12
+
13
+ /// <summary>
14
+ /// Integration tests for <see cref="{{name}}Repository"/>.
15
+ /// Uses in-memory database for isolation.
16
+ /// Follows SmartStack testing conventions: {Method}_When{Condition}_Should{Result}
17
+ /// </summary>
18
+ public class {{name}}RepositoryTests : IDisposable
19
+ {
20
+ private readonly ApplicationDbContext _context;
21
+ private readonly {{name}}Repository _sut;
22
+ {{#unless isSystemEntity}}
23
+ private readonly Guid _tenantId = Guid.NewGuid();
24
+ {{/unless}}
25
+
26
+ public {{name}}RepositoryTests()
27
+ {
28
+ var options = new DbContextOptionsBuilder<ApplicationDbContext>()
29
+ .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
30
+ .Options;
31
+
32
+ _context = new ApplicationDbContext(options);
33
+ _sut = new {{name}}Repository(_context);
34
+ }
35
+
36
+ public void Dispose()
37
+ {
38
+ _context.Dispose();
39
+ }
40
+
41
+ #region GetByIdAsync Tests
42
+
43
+ [Fact]
44
+ public async Task GetByIdAsync_WhenExists_ShouldReturnEntity()
45
+ {
46
+ // Arrange
47
+ var entity = CreateAndSave{{name}}();
48
+
49
+ // Act
50
+ var result = await _sut.GetByIdAsync(entity.Id);
51
+
52
+ // Assert
53
+ result.Should().NotBeNull();
54
+ result!.Id.Should().Be(entity.Id);
55
+ }
56
+
57
+ [Fact]
58
+ public async Task GetByIdAsync_WhenNotExists_ShouldReturnNull()
59
+ {
60
+ // Arrange
61
+ var nonExistentId = Guid.NewGuid();
62
+
63
+ // Act
64
+ var result = await _sut.GetByIdAsync(nonExistentId);
65
+
66
+ // Assert
67
+ result.Should().BeNull();
68
+ }
69
+
70
+ {{#if includeSoftDelete}}
71
+ [Fact]
72
+ public async Task GetByIdAsync_WhenSoftDeleted_ShouldReturnNull()
73
+ {
74
+ // Arrange
75
+ var entity = CreateAndSave{{name}}();
76
+ entity.SoftDelete("deleter@example.com");
77
+ await _context.SaveChangesAsync();
78
+
79
+ // Act
80
+ var result = await _sut.GetByIdAsync(entity.Id);
81
+
82
+ // Assert
83
+ result.Should().BeNull();
84
+ }
85
+
86
+ [Fact]
87
+ public async Task GetByIdAsync_WhenSoftDeletedAndIncludeDeleted_ShouldReturnEntity()
88
+ {
89
+ // Arrange
90
+ var entity = CreateAndSave{{name}}();
91
+ entity.SoftDelete("deleter@example.com");
92
+ await _context.SaveChangesAsync();
93
+
94
+ // Act
95
+ var result = await _sut.GetByIdAsync(entity.Id, includeDeleted: true);
96
+
97
+ // Assert
98
+ result.Should().NotBeNull();
99
+ result!.IsDeleted.Should().BeTrue();
100
+ }
101
+ {{/if}}
102
+
103
+ #endregion
104
+
105
+ #region GetAllAsync Tests
106
+
107
+ [Fact]
108
+ public async Task GetAllAsync_WhenEntitiesExist_ShouldReturnAll()
109
+ {
110
+ // Arrange
111
+ CreateAndSave{{name}}("CODE-001");
112
+ CreateAndSave{{name}}("CODE-002");
113
+ CreateAndSave{{name}}("CODE-003");
114
+
115
+ // Act
116
+ var result = await _sut.GetAllAsync();
117
+
118
+ // Assert
119
+ result.Should().HaveCount(3);
120
+ }
121
+
122
+ [Fact]
123
+ public async Task GetAllAsync_WhenEmpty_ShouldReturnEmptyList()
124
+ {
125
+ // Act
126
+ var result = await _sut.GetAllAsync();
127
+
128
+ // Assert
129
+ result.Should().BeEmpty();
130
+ }
131
+
132
+ {{#if includeSoftDelete}}
133
+ [Fact]
134
+ public async Task GetAllAsync_ShouldExcludeSoftDeleted()
135
+ {
136
+ // Arrange
137
+ CreateAndSave{{name}}("ACTIVE-001");
138
+ var deleted = CreateAndSave{{name}}("DELETED-001");
139
+ deleted.SoftDelete("deleter@example.com");
140
+ await _context.SaveChangesAsync();
141
+
142
+ // Act
143
+ var result = await _sut.GetAllAsync();
144
+
145
+ // Assert
146
+ result.Should().HaveCount(1);
147
+ result.Should().OnlyContain(x => !x.IsDeleted);
148
+ }
149
+ {{/if}}
150
+
151
+ #endregion
152
+
153
+ #region AddAsync Tests
154
+
155
+ [Fact]
156
+ public async Task AddAsync_WhenValidEntity_ShouldPersist()
157
+ {
158
+ // Arrange
159
+ {{#if isSystemEntity}}
160
+ var entity = {{name}}.Create("NEW-001", "test@example.com");
161
+ {{else}}
162
+ var entity = {{name}}.Create(_tenantId, "NEW-001", "test@example.com");
163
+ {{/if}}
164
+
165
+ // Act
166
+ await _sut.AddAsync(entity);
167
+ await _context.SaveChangesAsync();
168
+
169
+ // Assert
170
+ var persisted = await _context.Set<{{name}}>().FindAsync(entity.Id);
171
+ persisted.Should().NotBeNull();
172
+ persisted!.Code.Should().Be("NEW-001");
173
+ }
174
+
175
+ [Fact]
176
+ public async Task AddAsync_ShouldGenerateId()
177
+ {
178
+ // Arrange
179
+ {{#if isSystemEntity}}
180
+ var entity = {{name}}.Create("TEST", "test@example.com");
181
+ {{else}}
182
+ var entity = {{name}}.Create(_tenantId, "TEST", "test@example.com");
183
+ {{/if}}
184
+
185
+ // Act
186
+ await _sut.AddAsync(entity);
187
+ await _context.SaveChangesAsync();
188
+
189
+ // Assert
190
+ entity.Id.Should().NotBe(Guid.Empty);
191
+ }
192
+
193
+ #endregion
194
+
195
+ #region Update Tests
196
+
197
+ [Fact]
198
+ public async Task Update_WhenEntityModified_ShouldPersistChanges()
199
+ {
200
+ // Arrange
201
+ var entity = CreateAndSave{{name}}("ORIGINAL");
202
+ entity.Update("updater@example.com");
203
+
204
+ // Act
205
+ await _context.SaveChangesAsync();
206
+
207
+ // Assert
208
+ var updated = await _context.Set<{{name}}>().FindAsync(entity.Id);
209
+ updated!.UpdatedBy.Should().Be("updater@example.com");
210
+ }
211
+
212
+ #endregion
213
+
214
+ #region Remove Tests
215
+
216
+ [Fact]
217
+ public async Task Remove_WhenCalled_ShouldDeleteEntity()
218
+ {
219
+ // Arrange
220
+ var entity = CreateAndSave{{name}}();
221
+ var entityId = entity.Id;
222
+
223
+ // Act
224
+ _sut.Remove(entity);
225
+ await _context.SaveChangesAsync();
226
+
227
+ // Assert
228
+ var deleted = await _context.Set<{{name}}>().FindAsync(entityId);
229
+ deleted.Should().BeNull();
230
+ }
231
+
232
+ #endregion
233
+
234
+ #region ExistsByCodeAsync Tests
235
+
236
+ [Fact]
237
+ public async Task ExistsByCodeAsync_WhenExists_ShouldReturnTrue()
238
+ {
239
+ // Arrange
240
+ CreateAndSave{{name}}("EXISTING");
241
+
242
+ // Act
243
+ var result = await _sut.ExistsByCodeAsync("EXISTING");
244
+
245
+ // Assert
246
+ result.Should().BeTrue();
247
+ }
248
+
249
+ [Fact]
250
+ public async Task ExistsByCodeAsync_WhenNotExists_ShouldReturnFalse()
251
+ {
252
+ // Act
253
+ var result = await _sut.ExistsByCodeAsync("NON-EXISTENT");
254
+
255
+ // Assert
256
+ result.Should().BeFalse();
257
+ }
258
+
259
+ {{#if includeSoftDelete}}
260
+ [Fact]
261
+ public async Task ExistsByCodeAsync_WhenSoftDeleted_ShouldReturnFalse()
262
+ {
263
+ // Arrange
264
+ var entity = CreateAndSave{{name}}("DELETED");
265
+ entity.SoftDelete("deleter@example.com");
266
+ await _context.SaveChangesAsync();
267
+
268
+ // Act
269
+ var result = await _sut.ExistsByCodeAsync("DELETED");
270
+
271
+ // Assert
272
+ result.Should().BeFalse();
273
+ }
274
+ {{/if}}
275
+
276
+ #endregion
277
+
278
+ {{#unless isSystemEntity}}
279
+ #region Tenant Isolation Tests
280
+
281
+ [Fact]
282
+ public async Task GetAllAsync_ShouldOnlyReturnCurrentTenantEntities()
283
+ {
284
+ // Arrange
285
+ var tenantA = Guid.NewGuid();
286
+ var tenantB = Guid.NewGuid();
287
+
288
+ CreateAndSave{{name}}("A-001", tenantA);
289
+ CreateAndSave{{name}}("A-002", tenantA);
290
+ CreateAndSave{{name}}("B-001", tenantB);
291
+
292
+ // Create a tenant-scoped repository for tenant A
293
+ var tenantARepo = new {{name}}Repository(_context, tenantA);
294
+
295
+ // Act
296
+ var result = await tenantARepo.GetAllAsync();
297
+
298
+ // Assert
299
+ result.Should().HaveCount(2);
300
+ result.Should().OnlyContain(x => x.TenantId == tenantA);
301
+ }
302
+
303
+ [Fact]
304
+ public async Task GetByIdAsync_WhenDifferentTenant_ShouldReturnNull()
305
+ {
306
+ // Arrange
307
+ var tenantA = Guid.NewGuid();
308
+ var tenantB = Guid.NewGuid();
309
+ var entity = CreateAndSave{{name}}("TEST", tenantA);
310
+
311
+ var tenantBRepo = new {{name}}Repository(_context, tenantB);
312
+
313
+ // Act
314
+ var result = await tenantBRepo.GetByIdAsync(entity.Id);
315
+
316
+ // Assert
317
+ result.Should().BeNull("entity belongs to different tenant");
318
+ }
319
+
320
+ [Fact]
321
+ public async Task AddAsync_ShouldEnforceTenantId()
322
+ {
323
+ // Arrange
324
+ var entity = {{name}}.Create(_tenantId, "TEST", "test@example.com");
325
+
326
+ // Act
327
+ await _sut.AddAsync(entity);
328
+ await _context.SaveChangesAsync();
329
+
330
+ // Assert
331
+ var persisted = await _context.Set<{{name}}>().FindAsync(entity.Id);
332
+ persisted!.TenantId.Should().Be(_tenantId);
333
+ }
334
+
335
+ #endregion
336
+ {{/unless}}
337
+
338
+ #region Query Tests
339
+
340
+ [Fact]
341
+ public async Task FindByConditionAsync_ShouldFilterCorrectly()
342
+ {
343
+ // Arrange
344
+ CreateAndSave{{name}}("ALPHA");
345
+ CreateAndSave{{name}}("BETA");
346
+ CreateAndSave{{name}}("ALPHA-2");
347
+
348
+ // Act
349
+ var result = await _sut.FindByConditionAsync(x => x.Code.StartsWith("ALPHA"));
350
+
351
+ // Assert
352
+ result.Should().HaveCount(2);
353
+ result.Should().OnlyContain(x => x.Code.StartsWith("ALPHA"));
354
+ }
355
+
356
+ [Fact]
357
+ public async Task CountAsync_ShouldReturnCorrectCount()
358
+ {
359
+ // Arrange
360
+ CreateAndSave{{name}}("ONE");
361
+ CreateAndSave{{name}}("TWO");
362
+ CreateAndSave{{name}}("THREE");
363
+
364
+ // Act
365
+ var result = await _sut.CountAsync();
366
+
367
+ // Assert
368
+ result.Should().Be(3);
369
+ }
370
+
371
+ #endregion
372
+
373
+ #region Pagination Tests
374
+
375
+ [Fact]
376
+ public async Task GetPagedAsync_ShouldReturnCorrectPage()
377
+ {
378
+ // Arrange
379
+ for (int i = 1; i <= 25; i++)
380
+ {
381
+ CreateAndSave{{name}}($"CODE-{i:D3}");
382
+ }
383
+
384
+ // Act
385
+ var result = await _sut.GetPagedAsync(page: 2, pageSize: 10);
386
+
387
+ // Assert
388
+ result.Items.Should().HaveCount(10);
389
+ result.TotalCount.Should().Be(25);
390
+ result.TotalPages.Should().Be(3);
391
+ result.CurrentPage.Should().Be(2);
392
+ }
393
+
394
+ #endregion
395
+
396
+ #region Concurrency Tests
397
+
398
+ [Fact]
399
+ public async Task Update_WhenConcurrentModification_ShouldThrowException()
400
+ {
401
+ // Arrange
402
+ var entity = CreateAndSave{{name}}();
403
+
404
+ // Simulate concurrent modification
405
+ var concurrentContext = new ApplicationDbContext(
406
+ new DbContextOptionsBuilder<ApplicationDbContext>()
407
+ .UseInMemoryDatabase(_context.Database.GetDbConnection().Database)
408
+ .Options
409
+ );
410
+ var concurrentEntity = await concurrentContext.Set<{{name}}>().FindAsync(entity.Id);
411
+ concurrentEntity!.Update("concurrent@example.com");
412
+ await concurrentContext.SaveChangesAsync();
413
+
414
+ // Act
415
+ entity.Update("original@example.com");
416
+ var act = () => _context.SaveChangesAsync();
417
+
418
+ // Assert
419
+ await act.Should().ThrowAsync<DbUpdateConcurrencyException>();
420
+ }
421
+
422
+ #endregion
423
+
424
+ #region Helper Methods
425
+
426
+ private {{name}} CreateAndSave{{name}}(string code = "TEST-001"{{#unless isSystemEntity}}, Guid? tenantId = null{{/unless}})
427
+ {
428
+ {{#if isSystemEntity}}
429
+ var entity = {{name}}.Create(code, "test@example.com");
430
+ {{else}}
431
+ var entity = {{name}}.Create(tenantId ?? _tenantId, code, "test@example.com");
432
+ {{/if}}
433
+
434
+ _context.Set<{{name}}>().Add(entity);
435
+ _context.SaveChanges();
436
+
437
+ return entity;
438
+ }
439
+
440
+ #endregion
441
+ }