aw-spec-cli 0.1.0 → 0.1.2

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 (173) hide show
  1. package/assets/.agents/skills/aw-spec-addcomponent/SKILL.md +129 -0
  2. package/assets/.agents/skills/aw-spec-agent-context-update/SKILL.md +31 -0
  3. package/assets/.agents/skills/aw-spec-analyze/SKILL.md +296 -0
  4. package/assets/.agents/skills/aw-spec-checklist/SKILL.md +374 -0
  5. package/assets/.agents/skills/aw-spec-clarify/SKILL.md +320 -0
  6. package/assets/.agents/skills/aw-spec-constitution/SKILL.md +194 -0
  7. package/assets/.agents/skills/aw-spec-fast-implement/SKILL.md +268 -0
  8. package/assets/.agents/skills/aw-spec-foundation/SKILL.md +358 -0
  9. package/assets/.agents/skills/aw-spec-implement/SKILL.md +279 -0
  10. package/assets/.agents/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
  11. package/assets/.agents/skills/aw-spec-plan/SKILL.md +221 -0
  12. package/assets/.agents/skills/aw-spec-select-domain/SKILL.md +73 -0
  13. package/assets/.agents/skills/aw-spec-specify/SKILL.md +382 -0
  14. package/assets/.agents/skills/aw-spec-tasks/SKILL.md +259 -0
  15. package/assets/.agents/skills/aw-spec-taskstoissues/SKILL.md +107 -0
  16. package/assets/.aw-spec/domain.json +3 -0
  17. package/assets/.aw-spec/extensions/.registry +19 -0
  18. package/assets/.aw-spec/extensions/agent-context/README.md +57 -0
  19. package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +4 -0
  20. package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
  21. package/assets/.aw-spec/extensions/agent-context/extension.yml +34 -0
  22. package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
  23. package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
  24. package/assets/.aw-spec/extensions.yml +23 -0
  25. package/assets/.aw-spec/feature.json +3 -0
  26. package/assets/.aw-spec/init-options.json +9 -0
  27. package/assets/.aw-spec/integration.json +15 -0
  28. package/assets/.aw-spec/integrations/aw-spec.manifest.json +17 -0
  29. package/assets/.aw-spec/integrations/claude.manifest.json +17 -0
  30. package/assets/.aw-spec/memory/constitution.md +50 -0
  31. package/assets/.aw-spec/scripts/bash/check-prerequisites.sh +189 -0
  32. package/assets/.aw-spec/scripts/bash/common.sh +582 -0
  33. package/assets/.aw-spec/scripts/bash/create-new-feature.sh +299 -0
  34. package/assets/.aw-spec/scripts/bash/setup-plan.sh +84 -0
  35. package/assets/.aw-spec/scripts/bash/setup-tasks.sh +91 -0
  36. package/assets/.aw-spec/templates/checklist-template.md +40 -0
  37. package/assets/.aw-spec/templates/constitution-template.md +57 -0
  38. package/assets/.aw-spec/templates/plan-template.md +113 -0
  39. package/assets/.aw-spec/templates/spec-template.md +131 -0
  40. package/assets/.aw-spec/templates/tasks-template.md +252 -0
  41. package/assets/.aw-spec/workflows/aw-spec/workflow.yml +77 -0
  42. package/assets/.aw-spec/workflows/workflow-registry.json +13 -0
  43. package/assets/.claude/skills/aw-spec-addcomponent/SKILL.md +129 -0
  44. package/assets/.claude/skills/aw-spec-agent-context-update/SKILL.md +31 -0
  45. package/assets/.claude/skills/aw-spec-analyze/SKILL.md +296 -0
  46. package/assets/.claude/skills/aw-spec-checklist/SKILL.md +374 -0
  47. package/assets/.claude/skills/aw-spec-clarify/SKILL.md +320 -0
  48. package/assets/.claude/skills/aw-spec-constitution/SKILL.md +194 -0
  49. package/assets/.claude/skills/aw-spec-fast-implement/SKILL.md +268 -0
  50. package/assets/.claude/skills/aw-spec-foundation/SKILL.md +358 -0
  51. package/assets/.claude/skills/aw-spec-implement/SKILL.md +279 -0
  52. package/assets/.claude/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
  53. package/assets/.claude/skills/aw-spec-plan/SKILL.md +221 -0
  54. package/assets/.claude/skills/aw-spec-select-domain/SKILL.md +73 -0
  55. package/assets/.claude/skills/aw-spec-specify/SKILL.md +382 -0
  56. package/assets/.claude/skills/aw-spec-tasks/SKILL.md +259 -0
  57. package/assets/.claude/skills/aw-spec-taskstoissues/SKILL.md +107 -0
  58. package/assets/.kilocode/rules/specify-rules.md +4 -0
  59. package/assets/.kilocode/workflows/aw-spec-addcomponent.md +129 -0
  60. package/assets/.kilocode/workflows/aw-spec-agent-context.update.md +31 -0
  61. package/assets/.kilocode/workflows/aw-spec-analyze.md +296 -0
  62. package/assets/.kilocode/workflows/aw-spec-checklist.md +374 -0
  63. package/assets/.kilocode/workflows/aw-spec-clarify.md +320 -0
  64. package/assets/.kilocode/workflows/aw-spec-constitution.md +194 -0
  65. package/assets/.kilocode/workflows/aw-spec-fast-implement.md +263 -0
  66. package/assets/.kilocode/workflows/aw-spec-foundation.md +358 -0
  67. package/assets/.kilocode/workflows/aw-spec-implement.md +279 -0
  68. package/assets/.kilocode/workflows/aw-spec-minimal-implementation.md +204 -0
  69. package/assets/.kilocode/workflows/aw-spec-plan.md +221 -0
  70. package/assets/.kilocode/workflows/aw-spec-select-domain.md +73 -0
  71. package/assets/.kilocode/workflows/aw-spec-specify.md +382 -0
  72. package/assets/.kilocode/workflows/aw-spec-tasks.md +259 -0
  73. package/assets/.kilocode/workflows/aw-spec-taskstoissues.md +107 -0
  74. package/assets/docs/README.md +106 -0
  75. package/assets/docs/VerticalSlice/PACKAGING.md +185 -0
  76. package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +2833 -0
  77. package/assets/docs/VerticalSlice/pack-foundation.ps1 +145 -0
  78. package/assets/docs/VerticalSlice/pack-foundation.sh +196 -0
  79. package/assets/docs/VerticalSlice/unpack-foundation.ps1 +126 -0
  80. package/assets/docs/VerticalSlice/unpack-foundation.sh +217 -0
  81. package/assets/docs/architecture/clean-architecture.md +71 -0
  82. package/assets/docs/architecture/cqrs.md +141 -0
  83. package/assets/docs/architecture/eventing.md +83 -0
  84. package/assets/docs/architecture/folder-structure.md +61 -0
  85. package/assets/docs/architecture/modular-monolith.md +47 -0
  86. package/assets/docs/architecture/persistence.md +269 -0
  87. package/assets/docs/architecture/testing.md +55 -0
  88. package/assets/docs/architecture/vertical-slices.md +201 -0
  89. package/assets/docs/command.txt +295 -0
  90. package/assets/docs/components/Email/README.md +112 -0
  91. package/assets/docs/components/Email/integration.md +260 -0
  92. package/assets/docs/components/Jobs/README.md +113 -0
  93. package/assets/docs/components/Jobs/integration.md +245 -0
  94. package/assets/docs/components/QrGenerator/README.md +78 -0
  95. package/assets/docs/components/QrGenerator/integration.md +185 -0
  96. package/assets/docs/components/README.md +57 -0
  97. package/assets/docs/domain/domain-driven-design.md +212 -0
  98. package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +34 -0
  99. package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +17 -0
  100. package/assets/docs/domainspecs/healthcare/auditing.domain.md +29 -0
  101. package/assets/docs/domainspecs/healthcare/authorization.domain.md +26 -0
  102. package/assets/docs/domainspecs/healthcare/clinical-terms.md +20 -0
  103. package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +26 -0
  104. package/assets/docs/domainspecs/healthcare/comandrulemap.md +119 -0
  105. package/assets/docs/domainspecs/healthcare/consent.md +19 -0
  106. package/assets/docs/domainspecs/healthcare/cqrs.domain.md +12 -0
  107. package/assets/docs/domainspecs/healthcare/data-classification.domain.md +28 -0
  108. package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +11 -0
  109. package/assets/docs/domainspecs/healthcare/dicom.md +15 -0
  110. package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +28 -0
  111. package/assets/docs/domainspecs/healthcare/domain-requirements.md +55 -0
  112. package/assets/docs/domainspecs/healthcare/eventing.domain.md +18 -0
  113. package/assets/docs/domainspecs/healthcare/fhir.md +36 -0
  114. package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +23 -0
  115. package/assets/docs/domainspecs/healthcare/hipaa.md +34 -0
  116. package/assets/docs/domainspecs/healthcare/hl7.md +15 -0
  117. package/assets/docs/domainspecs/healthcare/integration-terms.md +21 -0
  118. package/assets/docs/domainspecs/healthcare/persistence.domain.md +20 -0
  119. package/assets/docs/domainspecs/healthcare/terminology.md +30 -0
  120. package/assets/docs/domainspecs/healthcare/testing.domain.md +15 -0
  121. package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +18 -0
  122. package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +12 -0
  123. package/assets/docs/domainspecs/pharma/ai-rules.domain.md +32 -0
  124. package/assets/docs/domainspecs/pharma/api-versioning.domain.md +20 -0
  125. package/assets/docs/domainspecs/pharma/atc.md +22 -0
  126. package/assets/docs/domainspecs/pharma/auditing.domain.md +26 -0
  127. package/assets/docs/domainspecs/pharma/authorization.domain.md +25 -0
  128. package/assets/docs/domainspecs/pharma/coding-standards.domain.md +28 -0
  129. package/assets/docs/domainspecs/pharma/comandrulemap.md +136 -0
  130. package/assets/docs/domainspecs/pharma/cqrs.domain.md +16 -0
  131. package/assets/docs/domainspecs/pharma/data-classification.domain.md +24 -0
  132. package/assets/docs/domainspecs/pharma/data-provenance.md +21 -0
  133. package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +11 -0
  134. package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +36 -0
  135. package/assets/docs/domainspecs/pharma/domain-requirements.md +99 -0
  136. package/assets/docs/domainspecs/pharma/eventing.domain.md +18 -0
  137. package/assets/docs/domainspecs/pharma/fhir.md +31 -0
  138. package/assets/docs/domainspecs/pharma/folder-structure.domain.md +24 -0
  139. package/assets/docs/domainspecs/pharma/hl7.md +17 -0
  140. package/assets/docs/domainspecs/pharma/idmp.md +26 -0
  141. package/assets/docs/domainspecs/pharma/integration-terms.md +21 -0
  142. package/assets/docs/domainspecs/pharma/ndc.md +28 -0
  143. package/assets/docs/domainspecs/pharma/persistence.domain.md +23 -0
  144. package/assets/docs/domainspecs/pharma/pharma-terms.md +27 -0
  145. package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +30 -0
  146. package/assets/docs/domainspecs/pharma/regulatory-compliance.md +19 -0
  147. package/assets/docs/domainspecs/pharma/terminology.md +32 -0
  148. package/assets/docs/domainspecs/pharma/testing.domain.md +19 -0
  149. package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +17 -0
  150. package/assets/docs/domainspecs/pharma/zero-trust.domain.md +12 -0
  151. package/assets/docs/glossary/ai-rules.md +59 -0
  152. package/assets/docs/governance/api-versioning.md +99 -0
  153. package/assets/docs/governance/coding-standards.md +87 -0
  154. package/assets/docs/governance/dependency-policy.md +40 -0
  155. package/assets/docs/interop/ihe-conformance.md +31 -0
  156. package/assets/docs/pharma-constitution.my-ref.md +86 -0
  157. package/assets/docs/requirement-spec.txt +15 -0
  158. package/assets/docs/security/auditing.md +117 -0
  159. package/assets/docs/security/authorization-matrix.md +14 -0
  160. package/assets/docs/security/authorization.md +87 -0
  161. package/assets/docs/security/data-classification.md +38 -0
  162. package/assets/docs/security/zero-trust.md +76 -0
  163. package/assets/docs/technology-constitution.md +172 -0
  164. package/dist/commands/init.d.ts.map +1 -1
  165. package/dist/commands/init.js +3 -2
  166. package/dist/commands/init.js.map +1 -1
  167. package/dist/utils/copy-assets.d.ts +8 -1
  168. package/dist/utils/copy-assets.d.ts.map +1 -1
  169. package/dist/utils/copy-assets.js +1 -0
  170. package/dist/utils/copy-assets.js.map +1 -1
  171. package/dist/utils/paths.js +1 -1
  172. package/dist/utils/paths.js.map +1 -1
  173. package/package.json +1 -1
@@ -0,0 +1,112 @@
1
+ # Email Component (`AppWeaver.Messaging.Email`)
2
+
3
+ > Pure technology (domain-agnostic). Reference sheet for the Email messaging component.
4
+ > To wire it into a project, follow the agent runbook in [integration.md](./integration.md).
5
+ > Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
6
+
7
+ ## What it is
8
+
9
+ A provider-agnostic email-sending component. Application code depends on a single abstraction,
10
+ `IEmailService`; the transport (Microsoft Graph, SMTP, SendGrid, OAuth2-SMTP, or a local File
11
+ sink) is chosen once during DI registration via `AddEmailMessaging(...)`.
12
+
13
+ **Use when:** a feature must send transactional or notification email (user-created alerts,
14
+ confirmations, admin notices, password flows, attachments).
15
+
16
+ ## Public contract (`AppWeaver.Messaging.Email`)
17
+
18
+ ```csharp
19
+ namespace AppWeaver.Messaging.Email;
20
+
21
+ public interface IEmailService
22
+ {
23
+ // Convenience overload — single recipient.
24
+ Task SendAsync(string from, string to, string subject, string body,
25
+ bool isHtml = true, CancellationToken cancellationToken = default);
26
+
27
+ // Full message — multi-recipient, Cc/Bcc, attachments.
28
+ Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default);
29
+ }
30
+ ```
31
+
32
+ ```csharp
33
+ namespace AppWeaver.Messaging.Email.Entities;
34
+
35
+ public class EmailMessage
36
+ {
37
+ public string From { get; set; } // falls back to provider DefaultFrom if empty
38
+ public List<string> To { get; set; } = new();
39
+ public List<string> Cc { get; set; } = new();
40
+ public List<string> Bcc { get; set; } = new();
41
+ public string Subject { get; set; }
42
+ public string Body { get; set; }
43
+ public bool IsHtml { get; set; } = true;
44
+ public List<EmailAttachment> Attachments { get; set; } = new();
45
+ }
46
+
47
+ public class EmailAttachment
48
+ {
49
+ public string FileName { get; set; }
50
+ public byte[] Content { get; set; }
51
+ public string ContentType { get; set; } = "application/octet-stream";
52
+ }
53
+ ```
54
+
55
+ ## Packages
56
+
57
+ | Package | Version | Role | Builder method | Provider key |
58
+ |---|---|---|---|---|
59
+ | `AppWeaver.Messaging.Email` | `0.1.3` | **Required.** Abstraction + `AddEmailMessaging` + facade. | — | — |
60
+ | `AppWeaver.Messaging.Email.MsGraph` | `0.1.2` | Microsoft 365 / Graph API (recommended for prod). | `UseMsGraph` | `MsGraph` |
61
+ | `AppWeaver.Messaging.Email.Smtp` | `0.1.3` | Classic SMTP (host/port/user/password). | `UseSmtp` | `Smtp` |
62
+ | `AppWeaver.Messaging.Email.SendGrid` | `0.1.2` | SendGrid API key. | `UseSendGrid` | `SendGrid` |
63
+ | `AppWeaver.Messaging.Email.OAuth2` | `0.1.3` | OAuth2-authenticated SMTP (O365). | `UseOAuth2` | `OAuth2` |
64
+ | `AppWeaver.Messaging.Email.File` | `0.1.2` | Writes email to a local `.txt` file — local dev / tests. | `UseFile` | `File` |
65
+
66
+ Install the abstraction **plus exactly one provider** for the environment you are wiring.
67
+
68
+ ## Provider option shapes
69
+
70
+ | Provider | Options type | Key fields | Bound config section |
71
+ |---|---|---|---|
72
+ | MsGraph | `MsGraphEmailOptions` | `TenantId`, `ClientId`, `ClientSecret`, `DefaultFrom`, `Scopes` | `Messaging.Email.MsGraph` |
73
+ | Smtp | `SmtpEmailOptions` | `Host`, `Port`, `Username`, `Password`, `EnableSsl`, `DefaultFrom` | `Messaging.Email.Smtp` |
74
+ | SendGrid | `SendGridEmailOptions` | `ApiKey` *(required)*, `DefaultFrom` | `Messaging.Email.SendGrid` |
75
+ | OAuth2 | `OAuth2EmailOptions` | `TenantId`, `ClientId`, `ClientSecret`, `Host`, `Port`, `Scopes`, `DefaultFrom` | `Messaging.Email.OAuth2` |
76
+ | File | `FileEmailOptions` | `DefaultFrom` | `Messaging.Email.Smtp` *(quirk — pass options inline)* |
77
+
78
+ ## Registration entry point
79
+
80
+ ```csharp
81
+ // In AppWeaver.Messaging.Email.Extensions
82
+ IComponentBuilder<IEmailService> AddEmailMessaging(
83
+ this IServiceCollection services,
84
+ Action<IComponentBuilder<IEmailService>> configureBuilder, // required
85
+ Action<MessagingEmailConfiguration>? configureOptions = null,// set Provider here
86
+ IConfiguration? configuration = null); // OR bind from config
87
+ ```
88
+
89
+ `MessagingEmailConfiguration` exposes a single `[Required] string Provider`. The facade
90
+ (`DefaultImplementation`) resolves the keyed provider whose key equals `Provider`.
91
+
92
+ ## Source map (framework)
93
+
94
+ ```
95
+ framework/Messaging.Email/
96
+ Integration.md ← original MsGraph tutorial (reference)
97
+ src/
98
+ AppWeaver.Messaging.Email/ ← IEmailService, EmailMessage, AddEmailMessaging, facade
99
+ AppWeaver.Messaging.Email.MsGraph/ ← UseMsGraph + MsGraphEmailService
100
+ AppWeaver.Messaging.Email.Smtp/ ← UseSmtp + SmtpEmailService
101
+ AppWeaver.Messaging.Email.SendGrid/ ← UseSendGrid + SendGridEmailService
102
+ AppWeaver.Messaging.Email.OAuth2/ ← UseOAuth2 + OAuth2EmailService
103
+ AppWeaver.Messaging.Email.File/ ← UseFile + FileEmailService
104
+ test/
105
+ MsGraphTest/Program.cs ← MsGraph end-to-end sample
106
+ TestApp/Program.cs ← OAuth2 + File multi-provider sample
107
+ ```
108
+
109
+ ## Related
110
+
111
+ - [integration.md](./integration.md) · [[components-readme]] · [[cqrs]] ·
112
+ [[clean-architecture]] · [[dependency-policy]]
@@ -0,0 +1,260 @@
1
+ # Email Component — Integration Workflow (agent playbook)
2
+
3
+ > Pure technology (domain-agnostic). This file is an **agent runbook**: read it top-to-bottom and
4
+ > execute the workflow in order. It drives an interactive integration of `IEmailService` into a
5
+ > project, asking the user which transport implementation(s) to wire. Read [README.md](./README.md)
6
+ > first for the contract. Replace `<SampleProjectName>` with the project name.
7
+ > Commands: `/aw-spec-implement`.
8
+
9
+ ---
10
+
11
+ ## Agent workflow — execute in order
12
+
13
+ 1. **Load context.** Read [README.md](./README.md) (contract + provider matrix) and this file.
14
+ 2. **Locate target projects** in the solution (see [[folder-structure]]):
15
+ `…Application` (injects `IEmailService`), `…Infrastructure` (does the registration),
16
+ `…Service` (holds `appsettings.json`). If the layout differs, map these roles before continuing.
17
+ 3. **Present the implementation catalog (below) as a selection list and ASK the user which
18
+ implementation(s) to integrate.** Use an interactive multi-select prompt (`AskUserQuestion`,
19
+ `multiSelect: true`). The list options are the rows of the *Implementation catalog*.
20
+ - The user MAY select **more than one** (e.g., `MsGraph` for production + `File` for local dev).
21
+ - If more than one is selected, **ask a follow-up single-select**: *which one is the default
22
+ (active) provider?* — this becomes `options.Provider`.
23
+ - If the user selects exactly one, that one is the default.
24
+ 4. **Apply “Common steps” once** (§Common steps) — abstraction package + registration scaffold.
25
+ 5. **For each selected implementation, apply its block** under §Implementation blocks — add its
26
+ package, its `Use…` call inside the `AddEmailMessaging` builder, and its config section.
27
+ 6. **Set the default provider:** `configureOptions: options => options.Provider = "<default key>"`.
28
+ All selected implementations stay registered; the active one is switchable later via config.
29
+ 7. **(Optional) Wire a usage example** in the relevant feature handler (§Usage).
30
+ 8. **Verify** (§Verification) and report what was added.
31
+
32
+ > **Guardrail — always set `options.Provider`.** When `Provider` is null the facade falls back to a
33
+ > **hardcoded `"SendGrid"`** key; if SendGrid is not registered, resolution throws at runtime. Never
34
+ > rely on the implicit fallback.
35
+
36
+ ---
37
+
38
+ ## Implementation catalog (selection list)
39
+
40
+ Present these as the multi-select options. `Key` is the provider key and the `Use…` method suffix.
41
+
42
+ | Key | Implementation | Best for | Package (v) | Required settings |
43
+ |---|---|---|---|---|
44
+ | `MsGraph` | Microsoft 365 / Graph API | Production on an O365 tenant | `AppWeaver.Messaging.Email.MsGraph` (0.1.2) | `TenantId`, `ClientId`, `ClientSecret`, `DefaultFrom` |
45
+ | `Smtp` | Classic SMTP | Any SMTP server | `AppWeaver.Messaging.Email.Smtp` (0.1.3) | `Host`, `Port`, `Username`, `Password`, `EnableSsl` |
46
+ | `SendGrid` | SendGrid API | SendGrid accounts | `AppWeaver.Messaging.Email.SendGrid` (0.1.2) | `ApiKey` *(required)*, `DefaultFrom` |
47
+ | `OAuth2` | OAuth2-authenticated SMTP | O365 SMTP with OAuth2 | `AppWeaver.Messaging.Email.OAuth2` (0.1.3) | `TenantId`, `ClientId`, `ClientSecret`, `Host`, `Port` |
48
+ | `File` | Writes email to a local `.txt` | Local dev / tests (no real send) | `AppWeaver.Messaging.Email.File` (0.1.2) | `DefaultFrom` |
49
+
50
+ > Recommended pairing: **`MsGraph` (default) + `File`** so developers can run locally without
51
+ > sending real mail, while production uses Graph.
52
+
53
+ ---
54
+
55
+ ## Common steps (apply once, regardless of selection)
56
+
57
+ ### C1 — Abstraction package version
58
+
59
+ **File:** `Directory.Packages.props` (versions live here only — see [[dependency-policy]])
60
+
61
+ ```xml
62
+ <ItemGroup>
63
+ <!-- AppWeaver — Email (abstraction) -->
64
+ <PackageVersion Include="AppWeaver.Messaging.Email" Version="0.1.3" />
65
+ <!-- + one <PackageVersion> per selected implementation — see each block below -->
66
+ </ItemGroup>
67
+ ```
68
+
69
+ ### C2 — Project references
70
+
71
+ **File:** `src/<SampleProjectName>.Application/<SampleProjectName>.Application.csproj`
72
+
73
+ ```xml
74
+ <ItemGroup>
75
+ <PackageReference Include="AppWeaver.Messaging.Email" /> <!-- no Version= -->
76
+ </ItemGroup>
77
+ ```
78
+
79
+ **File:** `src/<SampleProjectName>.Infrastructure/<SampleProjectName>.Infrastructure.csproj`
80
+ (add the abstraction here too, plus each selected implementation’s package — see its block).
81
+
82
+ ```xml
83
+ <ItemGroup>
84
+ <PackageReference Include="AppWeaver.Messaging.Email" />
85
+ </ItemGroup>
86
+ ```
87
+
88
+ ### C3 — Registration scaffold
89
+
90
+ **File:** `src/<SampleProjectName>.Infrastructure/Extensions/InfrastructureServiceRegistration.cs`
91
+
92
+ Add the base call; each selected implementation contributes one `builder.Use…(…)` line (C4).
93
+
94
+ ```csharp
95
+ using AppWeaver.Messaging.Email.Extensions;
96
+ // + one using per selected implementation (see its block)
97
+
98
+ services.AddEmailMessaging(
99
+ configureBuilder: builder =>
100
+ {
101
+ // ⬇ one Use…() call per selected implementation is inserted here
102
+ },
103
+ configureOptions: options => options.Provider = "<default key>"); // step 6
104
+ ```
105
+
106
+ ### C4 — Base appsettings block
107
+
108
+ **File:** `src/<SampleProjectName>.Service/appsettings.json`
109
+
110
+ ```jsonc
111
+ {
112
+ "Messaging": {
113
+ "Email": {
114
+ "DefaultFrom": "no-reply@your-domain.com",
115
+ "AdminEmail": "admin@your-domain.com"
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Implementation blocks
124
+
125
+ For each implementation the user selected, do all three sub-steps: **package**, **registration
126
+ line**, **config**. Secrets MUST be supplied via user-secrets / env / key vault — never committed.
127
+
128
+ ### MsGraph
129
+ ```xml
130
+ <!-- Directory.Packages.props --> <PackageVersion Include="AppWeaver.Messaging.Email.MsGraph" Version="0.1.2" />
131
+ <!-- Infrastructure.csproj --> <PackageReference Include="AppWeaver.Messaging.Email.MsGraph" />
132
+ ```
133
+ ```csharp
134
+ using AppWeaver.Messaging.Email.MsGraph.Extensions;
135
+ // inside AddEmailMessaging builder:
136
+ builder.UseMsGraph(o =>
137
+ {
138
+ o.TenantId = configuration["Messaging.Email.MsGraph:TenantId"];
139
+ o.ClientId = configuration["Messaging.Email.MsGraph:ClientId"];
140
+ o.ClientSecret = configuration["Messaging.Email.MsGraph:ClientSecret"]; // user-secrets
141
+ o.DefaultFrom = configuration["Messaging:Email:DefaultFrom"];
142
+ });
143
+ ```
144
+ ```jsonc
145
+ // appsettings.json — secret set out-of-band:
146
+ // dotnet user-secrets set "Messaging.Email.MsGraph:ClientSecret" "<secret>"
147
+ "Messaging.Email.MsGraph": { "TenantId": "<guid>", "ClientId": "<guid>", "DefaultFrom": "no-reply@your-domain.com" }
148
+ ```
149
+
150
+ ### Smtp
151
+ ```xml
152
+ <PackageVersion Include="AppWeaver.Messaging.Email.Smtp" Version="0.1.3" />
153
+ <PackageReference Include="AppWeaver.Messaging.Email.Smtp" />
154
+ ```
155
+ ```csharp
156
+ using AppWeaver.Messaging.Email.Smtp.Extensions;
157
+ builder.UseSmtp(o =>
158
+ {
159
+ o.Host = configuration["Messaging.Email.Smtp:Host"]!;
160
+ o.Port = int.Parse(configuration["Messaging.Email.Smtp:Port"] ?? "587");
161
+ o.Username = configuration["Messaging.Email.Smtp:Username"]!;
162
+ o.Password = configuration["Messaging.Email.Smtp:Password"]!; // user-secrets
163
+ o.EnableSsl = true;
164
+ o.DefaultFrom = configuration["Messaging:Email:DefaultFrom"];
165
+ });
166
+ ```
167
+ ```jsonc
168
+ "Messaging.Email.Smtp": { "Host": "smtp.example.com", "Port": 587, "Username": "<user>", "EnableSsl": true }
169
+ ```
170
+ > Note: the `SendAsync(from,to,…)` convenience overload of the SMTP service writes to a file; the
171
+ > `EmailMessage` overload performs the real SMTP send. Use `SendAsync(EmailMessage, ct)` for delivery.
172
+
173
+ ### SendGrid
174
+ ```xml
175
+ <PackageVersion Include="AppWeaver.Messaging.Email.SendGrid" Version="0.1.2" />
176
+ <PackageReference Include="AppWeaver.Messaging.Email.SendGrid" />
177
+ ```
178
+ ```csharp
179
+ using AppWeaver.Messaging.Email.SendGrid.Extensions;
180
+ builder.UseSendGrid(o =>
181
+ {
182
+ o.ApiKey = configuration["Messaging.Email.SendGrid:ApiKey"]!; // [Required] — user-secrets
183
+ o.DefaultFrom = configuration["Messaging:Email:DefaultFrom"];
184
+ });
185
+ ```
186
+ > `ApiKey` is `[Required]` and validates on startup — a missing key fails app boot, not the first send.
187
+
188
+ ### OAuth2
189
+ ```xml
190
+ <PackageVersion Include="AppWeaver.Messaging.Email.OAuth2" Version="0.1.3" />
191
+ <PackageReference Include="AppWeaver.Messaging.Email.OAuth2" />
192
+ ```
193
+ ```csharp
194
+ using AppWeaver.Messaging.Email.OAuth2.Extensions;
195
+ builder.UseOAuth2(o =>
196
+ {
197
+ o.TenantId = configuration["Messaging.Email.OAuth2:TenantId"];
198
+ o.ClientId = configuration["Messaging.Email.OAuth2:ClientId"];
199
+ o.ClientSecret = configuration["Messaging.Email.OAuth2:ClientSecret"]; // user-secrets
200
+ o.Host = "smtp.office365.com"; o.Port = 587;
201
+ o.DefaultFrom = configuration["Messaging:Email:DefaultFrom"];
202
+ });
203
+ ```
204
+
205
+ ### File
206
+ ```xml
207
+ <PackageVersion Include="AppWeaver.Messaging.Email.File" Version="0.1.2" />
208
+ <PackageReference Include="AppWeaver.Messaging.Email.File" />
209
+ ```
210
+ ```csharp
211
+ using AppWeaver.Messaging.Email.File.Extensions;
212
+ builder.UseFile(o => o.DefaultFrom = configuration["Messaging:Email:DefaultFrom"]);
213
+ ```
214
+ > Pass `DefaultFrom` **inline** as shown. The File provider’s config binder reads the
215
+ > `Messaging.Email.Smtp` section (a known quirk), so do not rely on a `Messaging.Email.File` config
216
+ > section. Output is written to `Emails_<yyyy-MM-dd>.txt` in `AppContext.BaseDirectory`.
217
+
218
+ ---
219
+
220
+ ## Usage (inject the abstraction in a feature handler)
221
+
222
+ Email is a side effect of a business operation — call it from the Application command handler that
223
+ owns the operation (see [[cqrs]], [[vertical-slices]]). Inject **`IEmailService`**, never a provider.
224
+
225
+ ```csharp
226
+ using AppWeaver.Messaging.Email;
227
+
228
+ public class CreateUserCommandHandler(IEmailService _email, IConfiguration _config /* … */)
229
+ : ICommandHandler<CreateUserCommand, Result<UserDto>>
230
+ {
231
+ public async ValueTask<Result<UserDto>> Handle(CreateUserCommand req, CancellationToken ct)
232
+ {
233
+ // … persist aggregate …
234
+ var from = _config["Messaging:Email:DefaultFrom"]!;
235
+ var to = _config["Messaging:Email:AdminEmail"]!;
236
+ await _email.SendAsync(from, to, $"New user: {req.Name}", "<p>Created.</p>", ct: ct);
237
+ return Result<UserDto>.Ok(/* dto */);
238
+ }
239
+ }
240
+ ```
241
+
242
+ Multi-recipient / attachments — use the `EmailMessage` overload (To/Cc/Bcc + `EmailAttachment`,
243
+ see [README.md](./README.md)).
244
+
245
+ ---
246
+
247
+ ## Verification
248
+
249
+ 1. **Builds** — `dotnet build` succeeds; no `Version=` on package references.
250
+ 2. **Startup validates** — app boots; options `ValidateOnStart()` passes; `options.Provider` equals
251
+ one of the selected keys.
252
+ 3. **Resolves** — `IEmailService` resolves from DI.
253
+ 4. **Sends** — a send succeeds; for `File`, confirm `Emails_<yyyy-MM-dd>.txt` appears in the app
254
+ base directory.
255
+ 5. **No secrets in diff** — `git diff` shows no secret / API key / password literals.
256
+
257
+ ## Related
258
+
259
+ - [README.md](./README.md) · [[components-readme]] · [[cqrs]] · [[clean-architecture]] ·
260
+ [[vertical-slices]] · [[dependency-policy]] · [[ai-rules]]
@@ -0,0 +1,113 @@
1
+ # Jobs Component (`AppWeaver.Jobs`)
2
+
3
+ > Pure technology (domain-agnostic). Reference sheet for the background-job component.
4
+ > To wire it into a project, follow the agent runbook in [integration.md](./integration.md).
5
+ > Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
6
+
7
+ ## What it is
8
+
9
+ A plug-and-play background-job framework. Application code depends only on the `IJobScheduler`
10
+ and `IJobProgress` abstractions; the engine (Quartz.NET, optionally with EF Core status
11
+ persistence) is a single registration decision. Supports **fire-and-forget**, **delayed**, and
12
+ **recurring (CRON)** jobs with progress tracking, status polling, and cooperative cancellation.
13
+
14
+ **Use when:** work must run outside the request thread — exports, batch processing, archival,
15
+ report generation, scheduled maintenance, sending bulk notifications.
16
+
17
+ ## Public contract (`AppWeaver.Jobs.Abstractions`)
18
+
19
+ ```csharp
20
+ public interface IJob { } // marker — implement on every job class
21
+
22
+ public interface IJobScheduler
23
+ {
24
+ Task<string> EnqueueAsync<TJob>(Expression<Func<TJob, Task>> jobMethod,
25
+ string? description = null, string? userId = null) where TJob : class, IJob;
26
+
27
+ Task<string> ScheduleAsync<TJob>(Expression<Func<TJob, Task>> jobMethod, TimeSpan delay,
28
+ string? description = null, string? userId = null) where TJob : class, IJob;
29
+
30
+ Task AddOrUpdateRecurringAsync<TJob>(string jobId,
31
+ Expression<Func<TJob, Task>> jobMethod, string cronExpression) where TJob : class, IJob;
32
+
33
+ Task RemoveRecurringAsync(string jobId);
34
+ Task<JobStatus?> GetStatusAsync(string jobId);
35
+ Task<IEnumerable<JobStatus>> GetStatusesAsync(string userId, DateTime? from = null, DateTime? to = null);
36
+ Task CancelAsync(string jobId);
37
+ }
38
+
39
+ public interface IJobProgress
40
+ {
41
+ Task ReportProgressAsync(int percent, string? message = null, object? progressDetails = null);
42
+ Task ReportResultUrlAsync(string url);
43
+ CancellationToken CancellationToken { get; } // check inside loops for cooperative cancel
44
+ }
45
+ ```
46
+
47
+ `JobStatus` carries: `JobId`, `State`, `CreatedAt`, `StartedAt`, `CompletedAt`, `ProgressPercent`,
48
+ `ProgressMessage`, `ResultUrl`, `ErrorMessage`, `Description`, `UserId`, `ProgressDetails`.
49
+ `JobState` = `Pending → Running → Completed | Failed | Cancelled`.
50
+
51
+ ## Packages
52
+
53
+ | Package | Version | Role | Builder method |
54
+ |---|---|---|---|
55
+ | `AppWeaver.Jobs` | `0.1.1` | **Required.** Abstractions + `AddAppWeaverJobs` + facade. | — |
56
+ | `AppWeaver.Jobs.Quartz` | `0.1.0` | **Recommended engine.** Quartz.NET scheduler; in-memory or persistent store. | `UseQuartzJobScheduler` |
57
+ | `AppWeaver.Jobs.Persistence.EfCore` | `0.1.0` | EF Core persistence of `JobStatus`. | `UseEfCoreJobs` + `UseJobDatabase` / `UseAppWeaverDataStore` |
58
+ | `AppWeaver.Jobs.InMemory` | `0.1.0` | ⚠️ **Work in progress — not functional. Do not use.** | — |
59
+
60
+ **Recommended stack:** `AppWeaver.Jobs` + `AppWeaver.Jobs.Quartz` + `AppWeaver.Jobs.Persistence.EfCore`.
61
+
62
+ ## Registration shapes
63
+
64
+ ```csharp
65
+ // In AppWeaver.Jobs.Extensions
66
+ IComponentBuilder<IJobScheduler> AddAppWeaverJobs(
67
+ this IServiceCollection services,
68
+ Action<IComponentBuilder<IJobScheduler>> configureBuilder, // required
69
+ Action<JobsConfiguration>? configureOptions = null, // set Provider = "Quartz"
70
+ IConfiguration? configuration = null); // OR bind "AppWeaver:Jobs"
71
+
72
+ // In AppWeaver.Jobs.Quartz.Extensions
73
+ builder.UseQuartzJobScheduler(opts => { opts.MaxConcurrency = 10; opts.ConfigureQuartz = q => { … }; });
74
+
75
+ // In AppWeaver.Jobs.Persistence.EfCore.Extensions (choose ONE store wiring)
76
+ builder.UseEfCoreJobs().UseJobDatabase(opts => opts.UseSqlite("Data Source=jobs.db")); // standalone
77
+ builder.UseEfCoreJobs().UseAppWeaverDataStore(); // shared DataStore
78
+ ```
79
+
80
+ `JobsConfiguration.Provider` (config section `AppWeaver:Jobs`) selects the engine key; for Quartz
81
+ it is `"Quartz"`. `UseQuartzJobScheduler` registers `IJobProgress` (`QuartzJobProgress`) and a
82
+ Quartz hosted service automatically.
83
+
84
+ ## Persistence modes (EF Core)
85
+
86
+ | Mode | Wiring | DbContext | Migration |
87
+ |---|---|---|---|
88
+ | **Standalone** | `.UseJobDatabase(opts => opts.UseXxx(conn))` | dedicated `JobStatusDbContext` | own migration / `EnsureCreated` |
89
+ | **Shared DataStore** | `.UseAppWeaverDataStore()` (after `AddDataStore().UseEfCoreDataStore()`) | shares DataStore connection | Shared: in `SharedComponentDbContext` migration; Isolated: own migration |
90
+
91
+ The Quartz **persistent store** (for distributed/durable scheduling) is configured separately via
92
+ `opts.ConfigureQuartz = q => q.UsePersistentStore(...)` and needs the Quartz DDL applied to the DB.
93
+ With no `ConfigureQuartz`, Quartz uses an in-memory store (single instance, lost on restart).
94
+
95
+ ## Source map (framework)
96
+
97
+ ```
98
+ framework/Jobs/
99
+ README.md ← full component manual (authoritative deep-dive)
100
+ src/
101
+ AppWeaver.Jobs/ ← IJob, IJobScheduler, IJobProgress, JobStatus, AddAppWeaverJobs
102
+ AppWeaver.Jobs.Quartz/ ← UseQuartzJobScheduler + QuartzJobScheduler/Wrapper/Progress
103
+ AppWeaver.Jobs.Persistence.EfCore/ ← UseEfCoreJobs/UseJobDatabase/UseAppWeaverDataStore
104
+ AppWeaver.Jobs.InMemory/ ← ⚠️ WIP, not functional
105
+ tests/
106
+ AppWeaver.Jobs.Tests/ ← DefaultJobSchedulerTests
107
+ TestApp/ ← Quartz + EF Core end-to-end scenarios (A–O)
108
+ ```
109
+
110
+ ## Related
111
+
112
+ - [integration.md](./integration.md) · [[components-readme]] · [[cqrs]] ·
113
+ [[persistence]] · [[clean-architecture]] · [[dependency-policy]]