@sha3/code 1.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 (165) hide show
  1. package/AGENTS.md +75 -0
  2. package/README.md +554 -0
  3. package/ai/adapters/codex.md +7 -0
  4. package/ai/adapters/copilot.md +7 -0
  5. package/ai/adapters/cursor.md +7 -0
  6. package/ai/adapters/windsurf.md +8 -0
  7. package/ai/constitution.md +12 -0
  8. package/bin/code-standards.mjs +47 -0
  9. package/biome.json +37 -0
  10. package/index.mjs +11 -0
  11. package/lib/cli/parse-args.mjs +416 -0
  12. package/lib/cli/post-run-guidance.mjs +43 -0
  13. package/lib/cli/run-init.mjs +123 -0
  14. package/lib/cli/run-profile.mjs +46 -0
  15. package/lib/cli/run-refactor.mjs +152 -0
  16. package/lib/cli/run-verify.mjs +67 -0
  17. package/lib/constants.mjs +167 -0
  18. package/lib/contract/load-rule-catalog.mjs +12 -0
  19. package/lib/contract/render-agents.mjs +79 -0
  20. package/lib/contract/render-contract-json.mjs +7 -0
  21. package/lib/contract/resolve-contract.mjs +52 -0
  22. package/lib/paths.mjs +50 -0
  23. package/lib/profile.mjs +108 -0
  24. package/lib/project/ai-instructions.mjs +28 -0
  25. package/lib/project/biome-ignore.mjs +14 -0
  26. package/lib/project/managed-files.mjs +105 -0
  27. package/lib/project/package-metadata.mjs +132 -0
  28. package/lib/project/prompt-files.mjs +111 -0
  29. package/lib/project/template-resolution.mjs +70 -0
  30. package/lib/refactor/materialize-refactor-context.mjs +106 -0
  31. package/lib/refactor/preservation-questions.mjs +33 -0
  32. package/lib/refactor/public-contract-extractor.mjs +22 -0
  33. package/lib/refactor/render-analysis-summary.mjs +50 -0
  34. package/lib/refactor/source-analysis.mjs +74 -0
  35. package/lib/utils/fs.mjs +220 -0
  36. package/lib/utils/prompts.mjs +63 -0
  37. package/lib/utils/text.mjs +43 -0
  38. package/lib/verify/change-audit-verifier.mjs +140 -0
  39. package/lib/verify/change-context.mjs +36 -0
  40. package/lib/verify/error-handling-verifier.mjs +164 -0
  41. package/lib/verify/explain-rule.mjs +54 -0
  42. package/lib/verify/issue-helpers.mjs +132 -0
  43. package/lib/verify/project-layout-verifier.mjs +259 -0
  44. package/lib/verify/project-verifier.mjs +267 -0
  45. package/lib/verify/readme-public-api.mjs +237 -0
  46. package/lib/verify/readme-verifier.mjs +216 -0
  47. package/lib/verify/render-json-report.mjs +3 -0
  48. package/lib/verify/render-text-report.mjs +34 -0
  49. package/lib/verify/source-analysis.mjs +126 -0
  50. package/lib/verify/source-rule-verifier.mjs +453 -0
  51. package/lib/verify/testing-verifier.mjs +113 -0
  52. package/lib/verify/tooling-verifier.mjs +82 -0
  53. package/lib/verify/typescript-style-verifier.mjs +407 -0
  54. package/package.json +55 -0
  55. package/profiles/default.profile.json +40 -0
  56. package/profiles/schema.json +96 -0
  57. package/prompts/init-contract.md +25 -0
  58. package/prompts/init-phase-2-implement.md +25 -0
  59. package/prompts/init-phase-3-verify.md +23 -0
  60. package/prompts/init.prompt.md +24 -0
  61. package/prompts/refactor-contract.md +26 -0
  62. package/prompts/refactor-phase-2-rebuild.md +25 -0
  63. package/prompts/refactor-phase-3-verify.md +24 -0
  64. package/prompts/refactor.prompt.md +26 -0
  65. package/resources/ai/AGENTS.md +18 -0
  66. package/resources/ai/adapters/codex.md +5 -0
  67. package/resources/ai/adapters/copilot.md +5 -0
  68. package/resources/ai/adapters/cursor.md +5 -0
  69. package/resources/ai/adapters/windsurf.md +5 -0
  70. package/resources/ai/contract.schema.json +68 -0
  71. package/resources/ai/rule-catalog.json +878 -0
  72. package/resources/ai/rule-catalog.schema.json +66 -0
  73. package/resources/ai/templates/adapters/codex.template.md +7 -0
  74. package/resources/ai/templates/adapters/copilot.template.md +7 -0
  75. package/resources/ai/templates/adapters/cursor.template.md +7 -0
  76. package/resources/ai/templates/adapters/windsurf.template.md +7 -0
  77. package/resources/ai/templates/agents.project.template.md +141 -0
  78. package/resources/ai/templates/examples/demo/src/billing/billing.service.ts +73 -0
  79. package/resources/ai/templates/examples/demo/src/config.ts +3 -0
  80. package/resources/ai/templates/examples/demo/src/invoice/invoice.errors.ts +51 -0
  81. package/resources/ai/templates/examples/demo/src/invoice/invoice.service.ts +96 -0
  82. package/resources/ai/templates/examples/demo/src/invoice/invoice.types.ts +9 -0
  83. package/resources/ai/templates/examples/rules/async-bad.ts +52 -0
  84. package/resources/ai/templates/examples/rules/async-good.ts +56 -0
  85. package/resources/ai/templates/examples/rules/class-first-bad.ts +36 -0
  86. package/resources/ai/templates/examples/rules/class-first-good.ts +74 -0
  87. package/resources/ai/templates/examples/rules/constructor-bad.ts +68 -0
  88. package/resources/ai/templates/examples/rules/constructor-good.ts +71 -0
  89. package/resources/ai/templates/examples/rules/control-flow-bad.ts +31 -0
  90. package/resources/ai/templates/examples/rules/control-flow-good.ts +54 -0
  91. package/resources/ai/templates/examples/rules/errors-bad.ts +42 -0
  92. package/resources/ai/templates/examples/rules/errors-good.ts +23 -0
  93. package/resources/ai/templates/examples/rules/functions-bad.ts +48 -0
  94. package/resources/ai/templates/examples/rules/functions-good.ts +58 -0
  95. package/resources/ai/templates/examples/rules/returns-bad.ts +38 -0
  96. package/resources/ai/templates/examples/rules/returns-good.ts +44 -0
  97. package/resources/ai/templates/examples/rules/testing-bad.ts +34 -0
  98. package/resources/ai/templates/examples/rules/testing-good.ts +54 -0
  99. package/resources/ai/templates/rules/architecture.md +41 -0
  100. package/resources/ai/templates/rules/async.md +13 -0
  101. package/resources/ai/templates/rules/class-first.md +45 -0
  102. package/resources/ai/templates/rules/control-flow.md +13 -0
  103. package/resources/ai/templates/rules/errors.md +18 -0
  104. package/resources/ai/templates/rules/functions.md +29 -0
  105. package/resources/ai/templates/rules/naming.md +13 -0
  106. package/resources/ai/templates/rules/readme.md +36 -0
  107. package/resources/ai/templates/rules/returns.md +13 -0
  108. package/resources/ai/templates/rules/testing.md +18 -0
  109. package/resources/ai/templates/rules.project.template.md +66 -0
  110. package/resources/ai/templates/skills/change-synchronization/SKILL.md +42 -0
  111. package/resources/ai/templates/skills/feature-shaping/SKILL.md +45 -0
  112. package/resources/ai/templates/skills/http-api-conventions/SKILL.md +171 -0
  113. package/resources/ai/templates/skills/init-workflow/SKILL.md +52 -0
  114. package/resources/ai/templates/skills/readme-authoring/SKILL.md +51 -0
  115. package/resources/ai/templates/skills/refactor-workflow/SKILL.md +50 -0
  116. package/resources/ai/templates/skills/simplicity-audit/SKILL.md +41 -0
  117. package/resources/ai/templates/skills/test-scope-selection/SKILL.md +50 -0
  118. package/resources/ai/templates/skills.index.template.md +25 -0
  119. package/standards/architecture.md +72 -0
  120. package/standards/changelog-policy.md +12 -0
  121. package/standards/manifest.json +36 -0
  122. package/standards/readme.md +56 -0
  123. package/standards/schema.json +124 -0
  124. package/standards/style.md +106 -0
  125. package/standards/testing.md +20 -0
  126. package/standards/tooling.md +38 -0
  127. package/templates/node-lib/.biomeignore +10 -0
  128. package/templates/node-lib/.vscode/extensions.json +1 -0
  129. package/templates/node-lib/.vscode/settings.json +9 -0
  130. package/templates/node-lib/README.md +172 -0
  131. package/templates/node-lib/biome.json +37 -0
  132. package/templates/node-lib/gitignore +6 -0
  133. package/templates/node-lib/package.json +32 -0
  134. package/templates/node-lib/scripts/release-publish.mjs +106 -0
  135. package/templates/node-lib/scripts/run-tests.mjs +65 -0
  136. package/templates/node-lib/src/config.ts +3 -0
  137. package/templates/node-lib/src/index.ts +2 -0
  138. package/templates/node-lib/src/logger.ts +7 -0
  139. package/templates/node-lib/src/package-info/package-info.service.ts +47 -0
  140. package/templates/node-lib/test/package-info.test.ts +10 -0
  141. package/templates/node-lib/tsconfig.build.json +1 -0
  142. package/templates/node-lib/tsconfig.json +5 -0
  143. package/templates/node-service/.biomeignore +10 -0
  144. package/templates/node-service/.vscode/extensions.json +1 -0
  145. package/templates/node-service/.vscode/settings.json +9 -0
  146. package/templates/node-service/README.md +244 -0
  147. package/templates/node-service/biome.json +37 -0
  148. package/templates/node-service/ecosystem.config.cjs +3 -0
  149. package/templates/node-service/gitignore +6 -0
  150. package/templates/node-service/package.json +42 -0
  151. package/templates/node-service/scripts/release-publish.mjs +106 -0
  152. package/templates/node-service/scripts/run-tests.mjs +65 -0
  153. package/templates/node-service/src/app/service-runtime.service.ts +57 -0
  154. package/templates/node-service/src/app-info/app-info.service.ts +47 -0
  155. package/templates/node-service/src/config.ts +11 -0
  156. package/templates/node-service/src/http/http-server.service.ts +66 -0
  157. package/templates/node-service/src/index.ts +2 -0
  158. package/templates/node-service/src/logger.ts +7 -0
  159. package/templates/node-service/src/main.ts +5 -0
  160. package/templates/node-service/test/service-runtime.test.ts +13 -0
  161. package/templates/node-service/tsconfig.build.json +1 -0
  162. package/templates/node-service/tsconfig.json +5 -0
  163. package/tsconfig/base.json +16 -0
  164. package/tsconfig/node-lib.json +5 -0
  165. package/tsconfig/node-service.json +1 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @section imports:externals
3
+ */
4
+
5
+ import { randomUUID } from "node:crypto";
6
+
7
+ /**
8
+ * @section consts
9
+ */
10
+
11
+ const serviceName = "invoice-service";
12
+
13
+ /**
14
+ * @section types
15
+ */
16
+
17
+ type CreateInvoiceCommand = { customerId: string; amount: number };
18
+ type Invoice = { id: string; customerId: string; amount: number; createdAt: Date };
19
+
20
+ /**
21
+ * @section class
22
+ */
23
+ export class InvoiceService {
24
+ /**
25
+ * @section private:attributes
26
+ */
27
+
28
+ private readonly requestId: string;
29
+ private readonly invoicesById: Map<string, Invoice>;
30
+
31
+ /**
32
+ * @section public:properties
33
+ */
34
+
35
+ public readonly serviceName: string;
36
+
37
+ /**
38
+ * @section constructor
39
+ */
40
+
41
+ public constructor() {
42
+ this.requestId = randomUUID();
43
+ this.invoicesById = new Map<string, Invoice>();
44
+ this.serviceName = serviceName;
45
+ }
46
+
47
+ /**
48
+ * @section factory
49
+ */
50
+
51
+ public static create(): InvoiceService {
52
+ const service = new InvoiceService();
53
+ return service;
54
+ }
55
+
56
+ /**
57
+ * @section private:methods
58
+ */
59
+
60
+ private toInvoice(command: CreateInvoiceCommand): Invoice {
61
+ const invoice: Invoice = { id: randomUUID(), customerId: command.customerId, amount: command.amount, createdAt: new Date() };
62
+ return invoice;
63
+ }
64
+
65
+ /**
66
+ * @section public:methods
67
+ */
68
+
69
+ public async create(command: CreateInvoiceCommand): Promise<Invoice> {
70
+ const invoice: Invoice = this.toInvoice(command);
71
+ this.invoicesById.set(invoice.id, invoice);
72
+ return invoice;
73
+ }
74
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @section consts
3
+ */
4
+
5
+ const fallbackPrefix = "INV";
6
+
7
+ /**
8
+ * @section types
9
+ */
10
+
11
+ type Clock = () => Date;
12
+ type IdFactory = () => string;
13
+
14
+ export class InvoiceIdBuilder {
15
+ /**
16
+ * @section private:attributes
17
+ */
18
+
19
+ private readonly clock: Clock;
20
+ private readonly idFactory: IdFactory;
21
+ private readonly prefix: string;
22
+
23
+ /**
24
+ * @section constructor
25
+ */
26
+
27
+ public constructor() {
28
+ // Bad: constructor wires concrete dependencies and environment details directly.
29
+ this.clock = () => new Date();
30
+ this.idFactory = () => Math.random().toString(36).slice(2, 8);
31
+ this.prefix = process.env.INVOICE_PREFIX || fallbackPrefix;
32
+ }
33
+
34
+ /**
35
+ * @section factory
36
+ */
37
+
38
+ public static create(): InvoiceIdBuilder {
39
+ const builder = new InvoiceIdBuilder();
40
+ return builder;
41
+ }
42
+
43
+ /**
44
+ * @section private:methods
45
+ */
46
+
47
+ private currentYear(): number {
48
+ const year = this.clock().getUTCFullYear();
49
+ return year;
50
+ }
51
+
52
+ /**
53
+ * @section public:methods
54
+ */
55
+
56
+ public build(): string {
57
+ const rawId = this.idFactory();
58
+ const year = this.currentYear();
59
+ const invoiceId = `${this.prefix}-${year}-${rawId}`;
60
+ return invoiceId;
61
+ }
62
+
63
+ /**
64
+ * @section static:methods
65
+ */
66
+
67
+ // empty
68
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @section consts
3
+ */
4
+
5
+ const defaultInvoicePrefix = "INV";
6
+
7
+ /**
8
+ * @section types
9
+ */
10
+
11
+ type Clock = () => Date;
12
+ type IdFactory = () => string;
13
+
14
+ /**
15
+ * @section class
16
+ */
17
+
18
+ export class InvoiceIdBuilder {
19
+ /**
20
+ * @section private:attributes
21
+ */
22
+
23
+ private readonly clock: Clock;
24
+ private readonly idFactory: IdFactory;
25
+ private readonly prefix: string;
26
+
27
+ /**
28
+ * @section constructor
29
+ */
30
+
31
+ public constructor(clock: Clock, idFactory: IdFactory, prefix = defaultInvoicePrefix) {
32
+ this.clock = clock;
33
+ this.idFactory = idFactory;
34
+ this.prefix = prefix;
35
+ }
36
+
37
+ /**
38
+ * @section factory
39
+ */
40
+
41
+ public static create(clock: Clock, idFactory: IdFactory): InvoiceIdBuilder {
42
+ const builder = new InvoiceIdBuilder(clock, idFactory);
43
+ return builder;
44
+ }
45
+
46
+ /**
47
+ * @section private:methods
48
+ */
49
+
50
+ private currentYear(): number {
51
+ const year = this.clock().getUTCFullYear();
52
+ return year;
53
+ }
54
+
55
+ /**
56
+ * @section public:methods
57
+ */
58
+
59
+ public build(): string {
60
+ const rawId = this.idFactory();
61
+ const year = this.currentYear();
62
+ const invoiceId = `${this.prefix}-${year}-${rawId}`;
63
+ return invoiceId;
64
+ }
65
+
66
+ /**
67
+ * @section static:methods
68
+ */
69
+
70
+ // empty
71
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ // empty
6
+
7
+ export class ControlFlowPolicy {
8
+ /**
9
+ * @section factory
10
+ */
11
+
12
+ public static create(): ControlFlowPolicy {
13
+ const policy = new ControlFlowPolicy();
14
+ return policy;
15
+ }
16
+
17
+ /**
18
+ * @section public:methods
19
+ */
20
+
21
+ public badSnippet(): string {
22
+ const snippet = "if (isEnabled) executeTask();";
23
+ return snippet;
24
+ }
25
+
26
+ /**
27
+ * @section static:methods
28
+ */
29
+
30
+ // empty
31
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type FeatureFlagMap = Record<string, boolean>;
6
+
7
+ /**
8
+ * @section class
9
+ */
10
+
11
+ export class FeatureGate {
12
+ /**
13
+ * @section private:attributes
14
+ */
15
+
16
+ private readonly flags: FeatureFlagMap;
17
+
18
+ /**
19
+ * @section constructor
20
+ */
21
+
22
+ public constructor(flags: FeatureFlagMap) {
23
+ this.flags = flags;
24
+ }
25
+
26
+ /**
27
+ * @section factory
28
+ */
29
+
30
+ public static from(flags: FeatureFlagMap): FeatureGate {
31
+ const gate = new FeatureGate(flags);
32
+ return gate;
33
+ }
34
+
35
+ /**
36
+ * @section public:methods
37
+ */
38
+
39
+ public canRunTask(key: string): boolean {
40
+ let enabled = false;
41
+
42
+ if (this.flags[key] === true) {
43
+ enabled = true;
44
+ }
45
+
46
+ return enabled;
47
+ }
48
+
49
+ /**
50
+ * @section static:methods
51
+ */
52
+
53
+ // empty
54
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ // empty
6
+
7
+ export class InvoiceLookup {
8
+ /**
9
+ * @section factory
10
+ */
11
+
12
+ public static create(): InvoiceLookup {
13
+ const lookup = new InvoiceLookup();
14
+ return lookup;
15
+ }
16
+
17
+ /**
18
+ * @section public:methods
19
+ */
20
+
21
+ public ensureInvoiceExists(invoiceId: string, exists: boolean): void {
22
+ try {
23
+ if (!exists) {
24
+ throw "missing";
25
+ }
26
+ } catch {
27
+ // bad: error is silently swallowed
28
+ }
29
+
30
+ if (!exists) {
31
+ throw new Error("failed");
32
+ }
33
+
34
+ console.log(invoiceId);
35
+ }
36
+
37
+ /**
38
+ * @section static:methods
39
+ */
40
+
41
+ // empty
42
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @section class
3
+ */
4
+ export class InvoiceLookup {
5
+ /**
6
+ * @section factory
7
+ */
8
+
9
+ public static create(): InvoiceLookup {
10
+ const lookup = new InvoiceLookup();
11
+ return lookup;
12
+ }
13
+
14
+ /**
15
+ * @section public:methods
16
+ */
17
+
18
+ public ensureInvoiceExists(invoiceId: string, exists: boolean): void {
19
+ if (!exists) {
20
+ throw new Error(`Invoice not found: invoiceId=${invoiceId}`);
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type PaymentInput = { amount?: number; currency?: string; metadata?: Record<string, string> };
6
+ type PersistedPaymentInput = PaymentInput & { normalizedAt: number; saved: boolean };
7
+
8
+ export class PaymentNormalizer {
9
+ /**
10
+ * @section factory
11
+ */
12
+
13
+ public static create(): PaymentNormalizer {
14
+ const normalizer = new PaymentNormalizer();
15
+ return normalizer;
16
+ }
17
+
18
+ /**
19
+ * @section public:methods
20
+ */
21
+
22
+ public normalize(input: PaymentInput): PersistedPaymentInput {
23
+ // Bad: too many responsibilities in one method.
24
+ const clone = { ...input };
25
+
26
+ if (!clone.currency) {
27
+ clone.currency = "usd";
28
+ }
29
+
30
+ if (typeof clone.currency === "string") {
31
+ clone.currency = clone.currency.toUpperCase();
32
+ }
33
+
34
+ if (!clone.metadata) {
35
+ clone.metadata = {};
36
+ }
37
+
38
+ clone.amount = Number(clone.amount || 0);
39
+ const persisted: PersistedPaymentInput = { ...clone, normalizedAt: Date.now(), saved: true };
40
+ return persisted;
41
+ }
42
+
43
+ /**
44
+ * @section static:methods
45
+ */
46
+
47
+ // empty
48
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type PaymentInput = { amount: number; currency: string; metadata: Record<string, string> };
6
+ type PaymentDraft = { amount: number; currency: string; metadata: Record<string, string> };
7
+
8
+ /**
9
+ * @section class
10
+ */
11
+
12
+ export class PaymentNormalizer {
13
+ /**
14
+ * @section factory
15
+ */
16
+
17
+ public static create(): PaymentNormalizer {
18
+ const normalizer = new PaymentNormalizer();
19
+ return normalizer;
20
+ }
21
+
22
+ /**
23
+ * @section private:methods
24
+ */
25
+
26
+ private normalizeAmount(amount: number): number {
27
+ const normalizedAmount = Number(amount.toFixed(2));
28
+ return normalizedAmount;
29
+ }
30
+
31
+ private normalizeCurrency(currency: string): string {
32
+ const normalizedCurrency = currency.trim().toUpperCase();
33
+ return normalizedCurrency;
34
+ }
35
+
36
+ private sanitizeMetadata(metadata: Record<string, string>): Record<string, string> {
37
+ const sanitizedMetadata: Record<string, string> = { ...metadata };
38
+ return sanitizedMetadata;
39
+ }
40
+
41
+ /**
42
+ * @section public:methods
43
+ */
44
+
45
+ public normalize(input: PaymentInput): PaymentDraft {
46
+ const amount: number = this.normalizeAmount(input.amount);
47
+ const currency: string = this.normalizeCurrency(input.currency);
48
+ const metadata: Record<string, string> = this.sanitizeMetadata(input.metadata);
49
+ const draft: PaymentDraft = { amount, currency, metadata };
50
+ return draft;
51
+ }
52
+
53
+ /**
54
+ * @section static:methods
55
+ */
56
+
57
+ // empty
58
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type InvoiceStatus = "paid" | "void" | "pending";
6
+
7
+ export class InvoiceStatusPresenter {
8
+ /**
9
+ * @section factory
10
+ */
11
+
12
+ public static create(): InvoiceStatusPresenter {
13
+ const presenter = new InvoiceStatusPresenter();
14
+ return presenter;
15
+ }
16
+
17
+ /**
18
+ * @section public:methods
19
+ */
20
+
21
+ public toStatusLabel(status: InvoiceStatus): string {
22
+ if (status === "paid") {
23
+ return "Paid";
24
+ }
25
+
26
+ if (status === "void") {
27
+ return "Void";
28
+ }
29
+
30
+ return "Pending";
31
+ }
32
+
33
+ /**
34
+ * @section static:methods
35
+ */
36
+
37
+ // empty
38
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type InvoiceStatus = "paid" | "void" | "pending";
6
+
7
+ /**
8
+ * @section class
9
+ */
10
+
11
+ export class InvoiceStatusPresenter {
12
+ /**
13
+ * @section factory
14
+ */
15
+
16
+ public static create(): InvoiceStatusPresenter {
17
+ const presenter = new InvoiceStatusPresenter();
18
+ return presenter;
19
+ }
20
+
21
+ /**
22
+ * @section public:methods
23
+ */
24
+
25
+ public toStatusLabel(status: InvoiceStatus): string {
26
+ let label: string;
27
+
28
+ if (status === "paid") {
29
+ label = "Paid";
30
+ } else if (status === "void") {
31
+ label = "Void";
32
+ } else {
33
+ label = "Pending";
34
+ }
35
+
36
+ return label;
37
+ }
38
+
39
+ /**
40
+ * @section static:methods
41
+ */
42
+
43
+ // empty
44
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @section types
3
+ */
4
+
5
+ type Invoice = { issuedAt: Date };
6
+
7
+ export class InvoiceEscalationPolicy {
8
+ /**
9
+ * @section factory
10
+ */
11
+
12
+ public static create(): InvoiceEscalationPolicy {
13
+ const policy = new InvoiceEscalationPolicy();
14
+ return policy;
15
+ }
16
+
17
+ /**
18
+ * @section public:methods
19
+ */
20
+
21
+ public evaluateEscalation(invoice: Invoice, now: Date): Promise<string> {
22
+ return Promise.resolve(invoice).then((current: Invoice) => {
23
+ const hasAge = now.getTime() - current.issuedAt.getTime() > 0;
24
+ const decision = hasAge ? "x" : "y";
25
+ return decision;
26
+ });
27
+ }
28
+
29
+ /**
30
+ * @section static:methods
31
+ */
32
+
33
+ // empty
34
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @section consts
3
+ */
4
+
5
+ const millisecondsPerDay = 24 * 60 * 60 * 1000;
6
+
7
+ /**
8
+ * @section types
9
+ */
10
+
11
+ type Invoice = { issuedAt: Date };
12
+ type EscalationDecision = "manual-review" | "no-escalation";
13
+
14
+ /**
15
+ * @section class
16
+ */
17
+
18
+ export class InvoiceEscalationPolicy {
19
+ /**
20
+ * @section factory
21
+ */
22
+
23
+ public static create(): InvoiceEscalationPolicy {
24
+ const policy = new InvoiceEscalationPolicy();
25
+ return policy;
26
+ }
27
+
28
+ /**
29
+ * @section private:methods
30
+ */
31
+
32
+ private daysBetween(from: Date, to: Date): number {
33
+ const diffInMilliseconds = to.getTime() - from.getTime();
34
+ const dayCount = Math.floor(diffInMilliseconds / millisecondsPerDay);
35
+ return dayCount;
36
+ }
37
+
38
+ /**
39
+ * @section public:methods
40
+ */
41
+
42
+ // Business rule: invoices older than 30 days are escalated for manual review.
43
+ public evaluateEscalation(invoice: Invoice, now: Date): EscalationDecision {
44
+ const ageInDays: number = this.daysBetween(invoice.issuedAt, now);
45
+ const decision: EscalationDecision = ageInDays > 30 ? "manual-review" : "no-escalation";
46
+ return decision;
47
+ }
48
+
49
+ /**
50
+ * @section static:methods
51
+ */
52
+
53
+ // empty
54
+ }
@@ -0,0 +1,41 @@
1
+ ### Feature-Folder Architecture (MUST)
2
+
3
+ - Code MUST be organized by feature (for example: `src/user`, `src/billing`, `src/invoice`).
4
+ - Feature folder names MUST be singular.
5
+ - Each feature MUST keep its own domain model, application services, and infrastructure adapters grouped by feature.
6
+ - Cross-feature imports MUST happen through explicit public entry points.
7
+ - `src/` MUST stay feature-first with `src/index.ts` and `src/<feature>/`.
8
+ - `src/app/` MUST exist only when the project has explicit composition/wiring that justifies it.
9
+ - `src/shared/` MUST exist only when cross-feature modules actually exist.
10
+ - Project layout MUST include `src/`, `test/`, `scripts/`, `docs/`, and `ai/` at minimum.
11
+ - For `node-service`, HTTP transport concerns SHOULD live in `src/http/` (`routes`, `controllers`, `middleware`).
12
+ - If a service needs to expose an HTTP API, it MUST use the latest published `hono` release line as the HTTP framework.
13
+ - For `node-lib`, API boundary separation MAY use `src/public/` and `src/internal/`.
14
+ - Feature files SHOULD use explicit role suffixes (`*.service.ts`, `*.repository.ts`, `*.schema.ts`, `*.mapper.ts`).
15
+ - Use `*.types.ts` only when shared feature types are substantial enough to justify a dedicated file.
16
+ - Inside `src/<feature>/`, files MUST expose exactly one public class unless the file is `*.types.ts`.
17
+ - Feature file domain base names SHOULD be singular (for example: `invoice.service.ts`).
18
+ - Ambiguous filenames (`utils.ts`, `helpers.ts`, `common.ts`) are forbidden for new feature code.
19
+ - Hardcoded, non-parameterized configuration MUST be centralized in `src/config.ts` (for example, external service URLs).
20
+ - `src/config.ts` MUST export a single default object named `config` and it MUST always be imported as `import config from ".../config.ts"`.
21
+ - Non-trivial structured output (for example pages, documents, markdown, HTML, prompts, emails, or report bodies) MUST be stored in dedicated template files instead of being composed inline in source code.
22
+ - Inline string building is allowed only for very small fragments where a template file would be disproportionate.
23
+ - Template rendering strategy MAY be as simple or as capable as needed; choose the smallest approach that keeps the content outside application code.
24
+ - Architecture decisions MUST default to the simplest structure that satisfies current requirements.
25
+ - New files, modules, or layers MUST be introduced only when they solve present complexity, not anticipated future needs.
26
+ - Simplicity is mandatory: if a design can be smaller, flatter, and more direct while staying correct, that simpler design MUST be chosen.
27
+ - Speculative abstractions, placeholder extension points, and gratuitous indirection are forbidden.
28
+ - Simplicity MUST NOT be interpreted as permission to remove valid architectural boundaries.
29
+ - Distinct responsibilities that are already justified SHOULD remain separated.
30
+
31
+ Good example:
32
+
33
+ - `ai/examples/demo/src/config.ts`
34
+ - `ai/examples/demo/src/invoice/invoice.service.ts`
35
+ - `ai/examples/demo/src/invoice/invoice.errors.ts`
36
+ - `ai/examples/demo/src/invoice/invoice.types.ts`
37
+ - `ai/examples/demo/src/billing/billing.service.ts`
38
+
39
+ Bad example:
40
+
41
+ - `ai/examples/rules/class-first-bad.ts` (mixes concerns and does not keep feature boundaries)
@@ -0,0 +1,13 @@
1
+ ### Async Policy (MUST)
2
+
3
+ - Asynchronous code MUST use `async/await`.
4
+ - `.then()`/`.catch()` chains are not allowed for new code.
5
+ - Every async call chain MUST have explicit error handling at the boundary.
6
+
7
+ Good example:
8
+
9
+ - `ai/examples/rules/async-good.ts`
10
+
11
+ Bad example:
12
+
13
+ - `ai/examples/rules/async-bad.ts`