@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,172 @@
1
+ # 📚 {{packageName}}
2
+
3
+ Small TypeScript library that exposes package metadata through one stable, package-root API.
4
+
5
+ ## TL;DR
6
+
7
+ ```bash
8
+ npm install
9
+ npm run check
10
+ npm run build
11
+ ```
12
+
13
+ ```ts
14
+ import { PackageInfoService } from "{{packageName}}";
15
+
16
+ const packageInfoService = PackageInfoService.createDefault();
17
+ console.log(packageInfoService.readPackageInfo());
18
+ ```
19
+
20
+ ## Why
21
+
22
+ Use this package when other modules, diagnostics code, or adapters need the package name without reaching into private files.
23
+ It gives consumers one stable import path and keeps the metadata mapping in a single service instead of scattering string literals through the codebase.
24
+
25
+ ## Main Capabilities
26
+
27
+ - Exposes package metadata through a stable package-root import.
28
+ - Keeps the configured package name behind one service instead of repeating config access in callers.
29
+ - Returns plain serializable data that is easy to log, test, or hand to HTTP or CLI adapters.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install {{packageName}}
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ```ts
40
+ import { PackageInfoService } from "{{packageName}}";
41
+
42
+ const packageInfoService = PackageInfoService.createDefault();
43
+ const packageInfo = packageInfoService.readPackageInfo();
44
+
45
+ console.log(packageInfo.packageName);
46
+ ```
47
+
48
+ This is the intended integration path: import from the package root, create the default service once, and reuse it anywhere you need package metadata.
49
+
50
+ ## Examples
51
+
52
+ Read package metadata for logging or diagnostics:
53
+
54
+ ```ts
55
+ import { PackageInfoService, type PackageInfo } from "{{packageName}}";
56
+
57
+ const packageInfoService = PackageInfoService.createDefault();
58
+ const packageInfo: PackageInfo = packageInfoService.readPackageInfo();
59
+
60
+ console.log(`[package] ${packageInfo.packageName}`);
61
+ ```
62
+
63
+ Create the default service once and reuse it across boundaries:
64
+
65
+ ```ts
66
+ import { PackageInfoService } from "{{packageName}}";
67
+
68
+ const packageInfoService = PackageInfoService.createDefault();
69
+ const firstRead = packageInfoService.readPackageInfo();
70
+ const secondRead = packageInfoService.readPackageInfo();
71
+ ```
72
+
73
+ ## Public API
74
+
75
+ ### `PackageInfoService`
76
+
77
+ Primary entrypoint for reading the package metadata exposed by the library.
78
+
79
+ ```ts
80
+ import { PackageInfoService } from "{{packageName}}";
81
+
82
+ const packageInfoService = PackageInfoService.createDefault();
83
+ ```
84
+
85
+ #### `createDefault()`
86
+
87
+ Creates a `PackageInfoService` using the package configuration.
88
+
89
+ Parameters:
90
+
91
+ - none
92
+
93
+ Returns:
94
+
95
+ - a ready-to-use `PackageInfoService`
96
+
97
+ Behavior notes:
98
+
99
+ - reads the default package name from `config.PACKAGE_NAME`
100
+ - keeps callers out of `src/config.ts`
101
+ - gives consumers a stable construction path that stays valid if the internals change
102
+
103
+ #### `readPackageInfo()`
104
+
105
+ Reads the package metadata exposed by the public API.
106
+
107
+ Parameters:
108
+
109
+ - none
110
+
111
+ Returns:
112
+
113
+ - a `PackageInfo` object containing the configured package name
114
+
115
+ Behavior notes:
116
+
117
+ - returns a plain serializable object
118
+ - performs no filesystem or network I/O
119
+ - is safe to call from application code, tests, and adapters
120
+
121
+ ### `PackageInfo`
122
+
123
+ Public type that describes the shape returned by `readPackageInfo()`.
124
+
125
+ ```ts
126
+ type PackageInfo = { packageName: string };
127
+ ```
128
+
129
+ ## Compatibility
130
+
131
+ - Node.js 20+
132
+ - ESM (`"type": "module"`)
133
+ - Strict TypeScript
134
+
135
+ ## Configuration
136
+
137
+ Configuration is centralized in `src/config.ts`.
138
+
139
+ - `config.PACKAGE_NAME`: package name returned by `readPackageInfo()` and exposed to consumers of this package.
140
+
141
+ ## Scripts
142
+
143
+ - `npm run standards:check`: verify deterministic project contract rules
144
+ - `npm run check`: standards + lint + format + typecheck + tests
145
+ - `npm run fix`: Biome autofix + format write
146
+ - `npm run build`: compile to `dist/`
147
+ - `npm run test`: run Node test runner with `tsx`
148
+
149
+ ## Structure
150
+
151
+ - `src/config.ts`: canonical hardcoded configuration
152
+ - `src/package-info/package-info.service.ts`: implementation of the public metadata service
153
+ - `src/index.ts`: public exports
154
+ - `test/package-info.test.ts`: behavior test
155
+
156
+ ## Troubleshooting
157
+
158
+ ### Import errors
159
+
160
+ Ensure the consumer project supports Node.js ESM and TypeScript extension rewriting.
161
+
162
+ ### Standards warnings
163
+
164
+ Run `npm run standards:check` for contract errors, README coverage problems, and advisory warnings from the project verifier.
165
+
166
+ ## AI Workflow
167
+
168
+ - Read `AGENTS.md`, `ai/contract.json`, and the relevant `ai/<assistant>.md` file before coding.
169
+ - Do not edit managed contract/tooling files during normal implementation.
170
+ - Keep this README focused on consumer-facing behavior, configuration, and public API.
171
+ - Rewrite examples and API notes whenever the exported behavior changes.
172
+ - Run `npm run check` before finalizing changes.
@@ -0,0 +1,37 @@
1
+ {
2
+ "files": {
3
+ "ignoreUnknown": true,
4
+ "ignore": [".code-standards", "dist"]
5
+ },
6
+ "formatter": {
7
+ "enabled": true,
8
+ "indentStyle": "space",
9
+ "indentWidth": 2,
10
+ "lineWidth": 160
11
+ },
12
+ "linter": {
13
+ "enabled": true,
14
+ "rules": {
15
+ "recommended": true,
16
+ "correctness": {
17
+ "noUnusedFunctionParameters": "error",
18
+ "noUnusedImports": "error",
19
+ "noUnusedPrivateClassMembers": "error",
20
+ "noUnusedVariables": "error"
21
+ }
22
+ }
23
+ },
24
+ "javascript": {
25
+ "formatter": {
26
+ "quoteStyle": "double",
27
+ "semicolons": "always",
28
+ "arrowParentheses": "always",
29
+ "bracketSpacing": true
30
+ }
31
+ },
32
+ "json": {
33
+ "formatter": {
34
+ "enabled": true
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
4
+ .code-standards/
5
+ *.tsbuildinfo
6
+ .DS_Store
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": ["dist"],
9
+ "scripts": {
10
+ "build": "tsc -p tsconfig.build.json",
11
+ "check": "npm run standards:check && npm run lint && npm run format:check && npm run typecheck && npm run test",
12
+ "fix": "npm run lint:fix && npm run format:write",
13
+ "lint": "biome check .",
14
+ "lint:fix": "biome check . --write",
15
+ "format:check": "biome format .",
16
+ "format:write": "biome format . --write",
17
+ "standards:check": "code-standards verify",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "node scripts/run-tests.mjs",
20
+ "publish": "node scripts/release-publish.mjs"
21
+ },
22
+ "dependencies": {
23
+ "@sha3/logger": "^2.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@sha3/code": "^{{packageVersion}}",
27
+ "@biomejs/biome": "^1.9.4",
28
+ "@types/node": "^22.13.10",
29
+ "tsx": "^4.19.3",
30
+ "typescript": "^5.8.2"
31
+ }
32
+ }
@@ -0,0 +1,106 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ function run(command, args, cwd, stdio = "inherit") {
7
+ return new Promise((resolve, reject) => {
8
+ const child = spawn(command, args, { cwd, stdio, shell: process.platform === "win32" });
9
+ let stdout = "";
10
+ let stderr = "";
11
+
12
+ if (stdio === "pipe") {
13
+ child.stdout.on("data", (chunk) => {
14
+ stdout += String(chunk);
15
+ });
16
+
17
+ child.stderr.on("data", (chunk) => {
18
+ stderr += String(chunk);
19
+ });
20
+ }
21
+
22
+ child.on("error", reject);
23
+ child.on("exit", (code) => {
24
+ resolve({ code: code ?? 1, stdout, stderr });
25
+ });
26
+ });
27
+ }
28
+
29
+ function isNotFoundError(output) {
30
+ const lower = output.toLowerCase();
31
+ return lower.includes("e404") || lower.includes("404 not found") || lower.includes("no match found");
32
+ }
33
+
34
+ function parseSemver(version) {
35
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
36
+ if (!match) {
37
+ throw new Error(`Unsupported version format: ${version}. Expected x.y.z`);
38
+ }
39
+
40
+ return { major: Number.parseInt(match[1], 10), minor: Number.parseInt(match[2], 10), patch: Number.parseInt(match[3], 10) };
41
+ }
42
+
43
+ function bumpMinor(version) {
44
+ const parsed = parseSemver(version);
45
+ return `${parsed.major}.${parsed.minor + 1}.0`;
46
+ }
47
+
48
+ async function readPackageJson(packageJsonPath) {
49
+ const raw = await readFile(packageJsonPath, "utf8");
50
+ return JSON.parse(raw);
51
+ }
52
+
53
+ async function writePackageJson(packageJsonPath, pkg) {
54
+ await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
55
+ }
56
+
57
+ async function versionExistsOnNpm(packageName, version, cwd) {
58
+ const result = await run("npm", ["view", `${packageName}@${version}`, "version", "--json"], cwd, "pipe");
59
+ const combined = `${result.stdout}\n${result.stderr}`;
60
+ if (result.code === 0) {
61
+ return true;
62
+ }
63
+ if (isNotFoundError(combined)) {
64
+ return false;
65
+ }
66
+
67
+ throw new Error(`Unable to verify npm version ${packageName}@${version}: ${combined.trim()}`);
68
+ }
69
+
70
+ async function main() {
71
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
72
+ const projectRoot = path.resolve(scriptDir, "..");
73
+ const packageJsonPath = path.join(projectRoot, "package.json");
74
+ const packageJson = await readPackageJson(packageJsonPath);
75
+ if (typeof packageJson.name !== "string" || packageJson.name.length === 0) {
76
+ throw new Error("package.json name is required.");
77
+ }
78
+ if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
79
+ throw new Error("package.json version is required.");
80
+ }
81
+
82
+ const exists = await versionExistsOnNpm(packageJson.name, packageJson.version, projectRoot);
83
+ if (exists) {
84
+ const nextVersion = bumpMinor(packageJson.version);
85
+ packageJson.version = nextVersion;
86
+ await writePackageJson(packageJsonPath, packageJson);
87
+ console.log(`Version ${packageJson.name}@${packageJson.version} already existed. Bumped to ${nextVersion}.`);
88
+ } else {
89
+ console.log(`Publishing new version ${packageJson.name}@${packageJson.version}.`);
90
+ }
91
+
92
+ const build = await run("npm", ["run", "build"], projectRoot);
93
+ if (build.code !== 0) {
94
+ throw new Error("npm run build failed.");
95
+ }
96
+
97
+ const publish = await run("npm", ["publish", "--access", "public", "--ignore-scripts"], projectRoot);
98
+ if (publish.code !== 0) {
99
+ throw new Error("npm publish failed.");
100
+ }
101
+ }
102
+
103
+ main().catch((error) => {
104
+ console.error(error instanceof Error ? error.message : String(error));
105
+ process.exit(1);
106
+ });
@@ -0,0 +1,65 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ const rootDir = process.cwd();
6
+ const testRoots = ["test", "src"];
7
+
8
+ async function collectTestsFrom(directoryPath, collector) {
9
+ let entries;
10
+
11
+ try {
12
+ entries = await readdir(directoryPath, { withFileTypes: true });
13
+ } catch (error) {
14
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
15
+ return;
16
+ }
17
+
18
+ throw error;
19
+ }
20
+
21
+ for (const entry of entries) {
22
+ const absolutePath = path.join(directoryPath, entry.name);
23
+ if (entry.isDirectory()) {
24
+ await collectTestsFrom(absolutePath, collector);
25
+ continue;
26
+ }
27
+ if (!entry.isFile()) {
28
+ continue;
29
+ }
30
+ if (entry.name.endsWith(".test.ts")) {
31
+ collector.push(path.relative(rootDir, absolutePath));
32
+ }
33
+ }
34
+ }
35
+
36
+ function runNodeTests(files) {
37
+ return new Promise((resolve, reject) => {
38
+ const args = ["--import", "tsx", "--test", ...files];
39
+ const child = spawn(process.execPath, args, { cwd: rootDir, stdio: "inherit", shell: process.platform === "win32" });
40
+
41
+ child.on("error", reject);
42
+ child.on("exit", (code) => {
43
+ resolve(code ?? 1);
44
+ });
45
+ });
46
+ }
47
+
48
+ async function main() {
49
+ const testFiles = [];
50
+
51
+ for (const root of testRoots) {
52
+ await collectTestsFrom(path.join(rootDir, root), testFiles);
53
+ }
54
+
55
+ const uniqueFiles = [...new Set(testFiles)].sort((left, right) => left.localeCompare(right));
56
+ const exitCode = await runNodeTests(uniqueFiles);
57
+ if (exitCode !== 0) {
58
+ process.exit(exitCode);
59
+ }
60
+ }
61
+
62
+ main().catch((error) => {
63
+ console.error(error instanceof Error ? error.message : String(error));
64
+ process.exit(1);
65
+ });
@@ -0,0 +1,3 @@
1
+ const config = { PACKAGE_NAME: "{{packageName}}" } as const;
2
+
3
+ export default config;
@@ -0,0 +1,2 @@
1
+ export { PackageInfoService } from "./package-info/package-info.service.ts";
2
+ export type { PackageInfo } from "./package-info/package-info.service.ts";
@@ -0,0 +1,7 @@
1
+ import Logger from "@sha3/logger";
2
+
3
+ const PACKAGE_NAME = "{{packageName}}";
4
+ const LOGGER_NAME = PACKAGE_NAME.startsWith("@") ? PACKAGE_NAME.split("/")[1] || PACKAGE_NAME : PACKAGE_NAME;
5
+ const logger = new Logger({ loggerName: LOGGER_NAME });
6
+
7
+ export default logger;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @section imports:internals
3
+ */
4
+
5
+ import config from "../config.ts";
6
+
7
+ /**
8
+ * @section types
9
+ */
10
+
11
+ export type PackageInfo = { packageName: string };
12
+
13
+ /**
14
+ * @section class
15
+ */
16
+
17
+ export class PackageInfoService {
18
+ /**
19
+ * @section private:attributes
20
+ */
21
+
22
+ private readonly packageName: string;
23
+
24
+ /**
25
+ * @section constructor
26
+ */
27
+
28
+ public constructor(packageName: string) {
29
+ this.packageName = packageName;
30
+ }
31
+
32
+ /**
33
+ * @section factory
34
+ */
35
+
36
+ public static createDefault(): PackageInfoService {
37
+ return new PackageInfoService(config.PACKAGE_NAME);
38
+ }
39
+
40
+ /**
41
+ * @section public:methods
42
+ */
43
+
44
+ public readPackageInfo(): PackageInfo {
45
+ return { packageName: this.packageName };
46
+ }
47
+ }
@@ -0,0 +1,10 @@
1
+ import * as assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+
4
+ import { PackageInfoService } from "../src/index.ts";
5
+
6
+ test("PackageInfoService exposes the configured package name", () => {
7
+ const packageInfoService = PackageInfoService.createDefault();
8
+
9
+ assert.deepEqual(packageInfoService.readPackageInfo(), { packageName: "{{packageName}}" });
10
+ });
@@ -0,0 +1 @@
1
+ { "extends": "./tsconfig.json", "compilerOptions": { "noEmit": false, "rootDir": "./src", "outDir": "./dist" }, "exclude": ["test/**/*.ts"] }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@sha3/code/tsconfig/node-lib.json",
3
+ "compilerOptions": { "types": ["node"], "rootDir": ".", "allowImportingTsExtensions": true, "rewriteRelativeImportExtensions": true },
4
+ "include": ["src/**/*.ts", "test/**/*.ts"]
5
+ }
@@ -0,0 +1,10 @@
1
+ node_modules
2
+ dist
3
+ coverage
4
+ .code-standards
5
+ AGENTS.md
6
+ PROMPT.md
7
+ SKILLS.md
8
+ ai
9
+ prompts
10
+ skills
@@ -0,0 +1 @@
1
+ { "recommendations": ["biomejs.biome"], "unwantedRecommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "rvest.vs-code-prettier-eslint"] }
@@ -0,0 +1,9 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.formatOnSaveMode": "file",
4
+ "editor.defaultFormatter": "biomejs.biome",
5
+ "[typescript]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
6
+ "[javascript]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
7
+ "[json]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true },
8
+ "[jsonc]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true }
9
+ }