@pukujan/create-modular-monolith 2.0.0 → 2.2.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 (97) hide show
  1. package/README.md +94 -23
  2. package/index.js +47 -0
  3. package/package.json +16 -19
  4. package/template/.cursor/commands/planning-study-log.md +25 -0
  5. package/template/.cursor/commands/pre-push-dev-log.md +52 -0
  6. package/template/.cursor/rules/api-documentation.mdc +21 -0
  7. package/template/.cursor/rules/file-exchange-inbox.mdc +29 -0
  8. package/template/.github/workflows/ci.yml +44 -0
  9. package/template/AGENTS.md +41 -0
  10. package/template/README.md +25 -55
  11. package/template/backend/.env.example +38 -0
  12. package/template/backend/package-lock.json +1118 -24
  13. package/template/backend/package.json +14 -4
  14. package/template/backend/scripts/check-module-boundaries.mjs +3 -0
  15. package/template/backend/src/modules/model-condenser/README.md +7 -0
  16. package/template/backend/src/modules/model-condenser/config/index.js +20 -0
  17. package/template/backend/src/modules/model-condenser/events/index.js +1 -0
  18. package/template/backend/src/modules/model-condenser/index.js +12 -0
  19. package/template/backend/src/modules/model-condenser/routes/health.routes.js +10 -0
  20. package/template/backend/src/modules/model-condenser/routes/index.js +10 -0
  21. package/template/backend/src/modules/model-condenser/routes/modelCondenser.routes.js +44 -0
  22. package/template/backend/src/modules/model-condenser/services/health.service.js +8 -0
  23. package/template/backend/src/modules/model-condenser/services/modelCondenser.facade.js +58 -0
  24. package/template/backend/src/modules/model-condenser/services/modelCondenser.service.js +513 -0
  25. package/template/backend/src/modules/model-condenser/tests/integration/modelCondenser.routes.test.js +40 -0
  26. package/template/backend/src/modules/model-condenser/tests/unit/modelCondenser.service.test.js +31 -0
  27. package/template/backend/src/modules/model-condenser/utils/index.js +1 -0
  28. package/template/backend/src/shared/contracts/consolidatedExports.contract.js +19 -0
  29. package/template/backend/src/shared/contracts/prePushDevLog.contract.js +28 -0
  30. package/template/backend/src/shared/domain/case-filing/core-models.js +117 -0
  31. package/template/backend/src/shared/http/errors.js +8 -0
  32. package/template/backend/src/shared/utils/consolidatedExport.js +30 -0
  33. package/template/backend/src/shared/utils/formatExchangeTimestamp.js +47 -0
  34. package/template/backend/src/shared/utils/formatExchangeTimestamp.test.js +30 -0
  35. package/template/backend/src/shared/utils/traceId.js +19 -0
  36. package/template/docs/API.md +42 -0
  37. package/template/docs/PUBLISHING.md +13 -1
  38. package/template/docs/README.md +7 -1
  39. package/template/docs/STARTER_PACK.md +4 -0
  40. package/template/docs/architecture/API_DOCUMENTATION_CONTRACT.md +112 -0
  41. package/template/docs/architecture/CONTRACTS_OVERVIEW.md +180 -0
  42. package/template/docs/architecture/EVAL_AND_CI.md +79 -0
  43. package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +2 -0
  44. package/template/docs/architecture/PLATFORM_ARCHITECTURE.md +221 -0
  45. package/template/docs/architecture/REPO_ARTIFACT_LAYOUT.md +33 -0
  46. package/template/docs/architecture/contracts/apiDocumentationRegistry.contract.md +40 -0
  47. package/template/docs/architecture/contracts/changelog.jsonl +12 -0
  48. package/template/docs/architecture/contracts/consolidatedExports.contract.md +58 -0
  49. package/template/docs/architecture/contracts/fileExchange.contract.md +47 -0
  50. package/template/docs/architecture/contracts/manifest.json +39 -0
  51. package/template/docs/architecture/contracts/prePushDevLog.contract.md +69 -0
  52. package/template/docs/model-condenser/API.md +102 -0
  53. package/template/file-exchange/README.md +41 -0
  54. package/template/file-exchange/exports/.gitkeep +0 -0
  55. package/template/file-exchange/exports/consolidated-models.json +625 -0
  56. package/template/file-exchange/imports/.gitkeep +0 -0
  57. package/template/frontend/.env.example +2 -0
  58. package/template/frontend/package-lock.json +125 -122
  59. package/template/frontend/package.json +1 -1
  60. package/template/frontend/src/index.css +311 -0
  61. package/template/frontend/src/modules/_reference/services/health-api.js +1 -1
  62. package/template/frontend/src/shared/api/client.js +67 -5
  63. package/template/models/.gitkeep +0 -0
  64. package/template/package.json +13 -4
  65. package/template/scripts/check-api-docs.mjs +183 -0
  66. package/template/scripts/condense-file-structure.mjs +44 -0
  67. package/template/scripts/condense-models.mjs +70 -0
  68. package/template/scripts/condense-prompts.mjs +161 -0
  69. package/template/scripts/consolidated-output.mjs +49 -0
  70. package/template/scripts/export-consolidated-models.mjs +11 -0
  71. package/template/scripts/git-hooks/pre-push.sample +15 -0
  72. package/template/scripts/import-to-file-exchange.mjs +43 -0
  73. package/template/scripts/lib/api-inventory.mjs +182 -0
  74. package/template/scripts/lib/dev-log-human-format.mjs +360 -0
  75. package/template/scripts/lib/git-snapshot.mjs +46 -0
  76. package/template/scripts/lib/module-scaffold.mjs +37 -1
  77. package/template/scripts/lib/repo-tree.mjs +127 -0
  78. package/template/scripts/lib/run-tests.mjs +60 -0
  79. package/template/scripts/lint-contracts.mjs +57 -0
  80. package/template/scripts/lint-repo-artifacts.mjs +37 -0
  81. package/template/scripts/new-module.mjs +7 -0
  82. package/template/scripts/resolve-import-stamp.mjs +50 -0
  83. package/template/scripts/verify-dev-log.mjs +50 -0
  84. package/template/scripts/write-pre-push-dev-log.mjs +220 -0
  85. package/template/work-log/INDEX.md +3 -0
  86. package/template/work-log/README.md +40 -0
  87. package/template/work-log/dev-logs/README.md +97 -0
  88. package/template/work-log/dev-logs/schemas/dev-log-agent.v1.schema.json +119 -0
  89. package/template/work-log/dev-logs/templates/dev-log-human.template.md +10 -0
  90. package/template/work-log/handoffs/README.md +36 -0
  91. package/template/work-log/study-docs/README.md +13 -0
  92. package/bin/create-modular-monolith.js +0 -132
  93. package/template/backend/src/modules/_reference/evals/README.md +0 -6
  94. package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +0 -12
  95. package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +0 -25
  96. package/template/scripts/sync-cli-template.mjs +0 -44
  97. /package/template/{frontend/src/modules → backend/db/migrations}/.gitkeep +0 -0
@@ -0,0 +1,119 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://legal-prmpt-eng.local/schemas/dev-log-agent.v1.json",
4
+ "title": "Agent audit dev log",
5
+ "description": "Machine-readable pre-push audit log for Cursor agents. Pair with human dev-log in work-log/dev-logs/human/.",
6
+ "type": "object",
7
+ "required": ["meta", "summary", "apis", "git", "tests", "repositoryTree", "changes", "decisions", "iterations", "tradeoffs", "improvements", "regressions", "risks", "followUps"],
8
+ "properties": {
9
+ "meta": {
10
+ "type": "object",
11
+ "required": ["schemaVersion", "entryId", "slug", "generatedAt", "humanLogPath", "audience"],
12
+ "properties": {
13
+ "schemaVersion": { "const": "1.0.0" },
14
+ "entryId": { "type": "string", "pattern": "^[0-9]{3}$" },
15
+ "slug": { "type": "string" },
16
+ "generatedAt": { "type": "string", "format": "date-time" },
17
+ "humanLogPath": { "type": "string" },
18
+ "audience": { "const": "agent" },
19
+ "filledBy": { "type": "string", "enum": ["script", "agent", "human"] },
20
+ "handoffRefs": { "type": "array", "items": { "type": "string" } }
21
+ }
22
+ },
23
+ "summary": { "type": "string" },
24
+ "apis": {
25
+ "type": "object",
26
+ "description": "HTTP routes, versioned contracts, deprecated surfaces",
27
+ "properties": {
28
+ "capturedAt": { "type": "string" },
29
+ "sourceDocs": { "type": "array", "items": { "type": "string" } },
30
+ "http": {
31
+ "type": "object",
32
+ "properties": {
33
+ "active": { "type": "array" },
34
+ "stub": { "type": "array" },
35
+ "deprecated": { "type": "array" }
36
+ }
37
+ },
38
+ "moduleStatus": { "type": "array" },
39
+ "versioned": { "type": "object" },
40
+ "deprecated": { "type": "object" },
41
+ "cli": { "type": "array" }
42
+ }
43
+ },
44
+ "git": {
45
+ "type": "object",
46
+ "properties": {
47
+ "branch": { "type": "string" },
48
+ "sha": { "type": "string" },
49
+ "shortSha": { "type": "string" },
50
+ "changedFiles": { "type": "array" },
51
+ "diffStatAgainstHead": { "type": "string" },
52
+ "recentCommits": { "type": "array", "items": { "type": "string" } }
53
+ }
54
+ },
55
+ "tests": {
56
+ "type": "object",
57
+ "properties": {
58
+ "ran": { "type": "boolean" },
59
+ "exitCode": { "type": ["integer", "null"] },
60
+ "summary": { "type": "string" },
61
+ "passed": { "type": "array", "items": { "type": "string" } },
62
+ "failed": { "type": "array", "items": { "type": "string" } },
63
+ "commands": { "type": "array", "items": { "type": "string" } }
64
+ }
65
+ },
66
+ "repositoryTree": {
67
+ "type": "object",
68
+ "properties": {
69
+ "capturedAt": { "type": "string" },
70
+ "excludeDirs": { "type": "array", "items": { "type": "string" } },
71
+ "treeIgnoreFlag": { "type": "string" },
72
+ "stats": { "type": "object" },
73
+ "treeText": { "type": "string" },
74
+ "flatPathCount": { "type": "integer" }
75
+ }
76
+ },
77
+ "changes": {
78
+ "type": "object",
79
+ "properties": {
80
+ "byArea": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } },
81
+ "added": { "type": "array", "items": { "type": "string" } },
82
+ "modified": { "type": "array", "items": { "type": "string" } },
83
+ "deleted": { "type": "array", "items": { "type": "string" } },
84
+ "narrative": { "type": "array", "items": { "type": "string" } }
85
+ }
86
+ },
87
+ "decisions": {
88
+ "type": "array",
89
+ "items": {
90
+ "type": "object",
91
+ "required": ["id", "decision", "rationale"],
92
+ "properties": {
93
+ "id": { "type": "string" },
94
+ "decision": { "type": "string" },
95
+ "rationale": { "type": "string" },
96
+ "alternativesRejected": { "type": "array", "items": { "type": "string" } },
97
+ "tradeoff": { "type": "string" }
98
+ }
99
+ }
100
+ },
101
+ "iterations": {
102
+ "type": "array",
103
+ "items": {
104
+ "type": "object",
105
+ "properties": {
106
+ "attempt": { "type": "integer" },
107
+ "action": { "type": "string" },
108
+ "outcome": { "type": "string" },
109
+ "blockedBy": { "type": "string" }
110
+ }
111
+ }
112
+ },
113
+ "tradeoffs": { "type": "array", "items": { "type": "string" } },
114
+ "improvements": { "type": "array", "items": { "type": "string" } },
115
+ "regressions": { "type": "array", "items": { "type": "string" } },
116
+ "risks": { "type": "array", "items": { "type": "string" } },
117
+ "followUps": { "type": "array", "items": { "type": "string" } }
118
+ }
119
+ }
@@ -0,0 +1,10 @@
1
+ # Dev log (human) — two-part layout
2
+
3
+ Generated by `npm run dev-log:pre-push`. Do not edit this file directly.
4
+
5
+ The generator builds:
6
+
7
+ 1. **Part I — Summary** (top): TOC, mermaid diagrams, audit tables (API, versions/prompts, tests, git, condensed tree).
8
+ 2. **Part II — Detailed** (bottom): decisions, iterations, full API registry, full git snapshot, full tree.
9
+
10
+ See `scripts/lib/dev-log-human-format.mjs`.
@@ -0,0 +1,36 @@
1
+ # Handoffs
2
+
3
+ Implementation specs and starter packs. Parent folder: [work-log](../README.md).
4
+
5
+ Study logs and planning notes live in **[../study-docs/](../study-docs/)** — not here.
6
+
7
+ ## Layout
8
+
9
+ ```text
10
+ work-log/handoffs/
11
+ 001_*_starter_*/ ← starter pack (folder)
12
+ 002_*_handoff_*.md
13
+ 005_*_handoff_*.md ← original, v2, v3
14
+ ```
15
+
16
+ Full index: [../INDEX.md](../INDEX.md).
17
+
18
+ ## Filename convention
19
+
20
+ ```text
21
+ {NNN}_{YYYY-MM-DD}_{HH-MM}_{kind}_{short-slug}.md
22
+ ```
23
+
24
+ | Part | Example |
25
+ |------|---------|
26
+ | Date | `2026-05-23` |
27
+ | Time | `10-49` (24h, optional) |
28
+ | Kind | `handoff`, `handoff-v2`, `handoff-v3`, `handoff-original` |
29
+
30
+ Same pattern in [study-docs](../study-docs/README.md) (`study-log`) and [dev-logs](../dev-logs/README.md) (`dev-log`).
31
+
32
+ ## 005 execution order
33
+
34
+ 1. [Original](./005_2026-05-23_10-49_handoff-original_parsed-cache-rule-authority.md) — requirements
35
+ 2. [v3](./005_2026-05-23_11-20_handoff-v3_filing-structure-architecture.md) — architecture first
36
+ 3. [v2](./005_2026-05-23_11-14_handoff-v2_planned-review-in-cursor.md) — pipeline implementation
@@ -0,0 +1,13 @@
1
+ # Study docs
2
+
3
+ Design rationale, planning conversations, and portfolio study logs. Parent: [work-log](../README.md).
4
+
5
+ Implementation specs are in **[../handoffs/](../handoffs/)**.
6
+
7
+ ## Filename convention
8
+
9
+ Same as [handoffs](../handoffs/README.md) and [dev-logs](../dev-logs/README.md):
10
+
11
+ `{NNN}_{YYYY-MM-DD}_{HH-MM}_study-log_{slug}.md`
12
+
13
+ Full index: [../INDEX.md](../INDEX.md).
@@ -1,132 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- cpSync,
4
- existsSync,
5
- mkdirSync,
6
- readFileSync,
7
- readdirSync,
8
- writeFileSync
9
- } from "fs";
10
- import { join, resolve, basename } from "path";
11
- import { fileURLToPath } from "url";
12
- import { spawnSync } from "child_process";
13
-
14
- const __dirname = fileURLToPath(new URL(".", import.meta.url));
15
- const templateDir = join(__dirname, "..", "template");
16
-
17
- const args = process.argv.slice(2).filter((a) => a !== "--");
18
- const installDeps = args.includes("--install");
19
- const initGit = args.includes("--git");
20
- const positional = args.filter((a) => !a.startsWith("--"));
21
-
22
- const targetArg = positional[0];
23
- const cwd = process.cwd();
24
- const targetDir = targetArg ? resolve(cwd, targetArg) : resolve(cwd, "my-platform");
25
- const projectName = basename(targetDir);
26
-
27
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(projectName)) {
28
- console.error(
29
- "Project folder name must be kebab-case (example: my-platform).\n",
30
- "Usage: npm create @pukujan/modular-monolith@2 my-platform"
31
- );
32
- process.exit(1);
33
- }
34
-
35
- if (!existsSync(templateDir)) {
36
- console.error("Template missing. Reinstall @pukujan/create-modular-monolith.");
37
- process.exit(1);
38
- }
39
-
40
- if (existsSync(targetDir)) {
41
- try {
42
- const entries = readdirSync(targetDir);
43
- if (entries.length > 0) {
44
- console.error(`Target directory is not empty: ${targetDir}`);
45
- process.exit(1);
46
- }
47
- } catch {
48
- console.error(`Cannot use target: ${targetDir}`);
49
- process.exit(1);
50
- }
51
- }
52
-
53
- const displayName = toTitleCase(projectName);
54
-
55
- console.log(`\nCreating modular monolith starter: ${projectName}`);
56
- console.log(` → ${targetDir}\n`);
57
-
58
- mkdirSync(targetDir, { recursive: true });
59
- cpSync(templateDir, targetDir, { recursive: true });
60
-
61
- replaceInTree(targetDir, [
62
- ["modular-monolith-starter-backend", `${projectName}-backend`],
63
- ["modular-monolith-starter-frontend", `${projectName}-frontend`],
64
- ["modular-monolith-starter", projectName],
65
- ["Modular Monolith Platform Starter", displayName]
66
- ]);
67
-
68
- if (installDeps) {
69
- console.log("Installing dependencies…");
70
- run("npm", ["install"], join(targetDir, "backend"));
71
- run("npm", ["install"], join(targetDir, "frontend"));
72
- }
73
-
74
- if (initGit) {
75
- run("git", ["init"], targetDir);
76
- run("git", ["add", "."], targetDir);
77
- run("git", ["commit", "-m", "chore: scaffold modular monolith starter"], targetDir);
78
- }
79
-
80
- console.log("\nDone.\n");
81
- console.log("Next steps:");
82
- console.log(` cd ${targetArg || projectName}`);
83
- if (!installDeps) {
84
- console.log(" cd backend && npm install");
85
- console.log(" cd ../frontend && npm install");
86
- }
87
- console.log(" npm run dev:backend # terminal 1");
88
- console.log(" npm run dev:frontend # terminal 2");
89
- console.log(` npm run new:module -- my-feature --label "My Feature"`);
90
- console.log(" npm run lint:architecture && npm test\n");
91
-
92
- function replaceInTree(dir, pairs) {
93
- for (const file of walkFiles(dir)) {
94
- if (!/\.(json|md|js|jsx|mjs|html)$/.test(file)) continue;
95
- let content = readFileSync(file, "utf8");
96
- let changed = false;
97
- for (const [from, to] of pairs) {
98
- if (content.includes(from)) {
99
- content = content.split(from).join(to);
100
- changed = true;
101
- }
102
- }
103
- if (changed) writeFileSync(file, content, "utf8");
104
- }
105
- }
106
-
107
- function walkFiles(dir, acc = []) {
108
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
109
- const full = join(dir, entry.name);
110
- if (entry.isDirectory()) {
111
- if (entry.name === "node_modules") continue;
112
- walkFiles(full, acc);
113
- } else {
114
- acc.push(full);
115
- }
116
- }
117
- return acc;
118
- }
119
-
120
- function toTitleCase(value) {
121
- return value
122
- .split("-")
123
- .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
124
- .join(" ");
125
- }
126
-
127
- function run(cmd, cmdArgs, dir) {
128
- const result = spawnSync(cmd, cmdArgs, { cwd: dir, stdio: "inherit" });
129
- if (result.status !== 0) {
130
- console.warn(`Warning: ${cmd} ${cmdArgs.join(" ")} exited with ${result.status}`);
131
- }
132
- }
@@ -1,6 +0,0 @@
1
- # Evals — _reference
2
-
3
- - **datasets/** — fixtures (input, expected constraints).
4
- - **runners/** — `*.eval.mjs` files executed via `npm run test:evals`.
5
-
6
- Run: `npm run test:evals -- _reference`
@@ -1,12 +0,0 @@
1
- {
2
- "cases": [
3
- {
4
- "id": "health-shape",
5
- "description": "Health payload includes module name",
6
- "input": {},
7
- "expect": {
8
- "status": "ok"
9
- }
10
- }
11
- ]
12
- }
@@ -1,25 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFileSync } from "fs";
4
- import { join, dirname } from "path";
5
- import { fileURLToPath } from "url";
6
- import { getHealth } from "../../services/health.service.js";
7
- import { renderPrompt } from "../../../../shared/ai/prompt-registry.js";
8
- import * as examplePrompt from "../../prompts/templates/example.prompt.js";
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
-
12
- test("_reference: health service matches dataset", () => {
13
- const dataset = JSON.parse(
14
- readFileSync(join(__dirname, "../datasets/example.cases.json"), "utf8")
15
- );
16
- const expected = dataset.cases[0].expect;
17
- const result = getHealth({ name: "_reference" });
18
- assert.equal(result.status, expected.status);
19
- assert.equal(result.module, "_reference");
20
- });
21
-
22
- test("_reference: example prompt renders variables", () => {
23
- const rendered = renderPrompt(examplePrompt.template, { matterId: "MAT-001" });
24
- assert.match(rendered, /MAT-001/);
25
- });
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copies the v2 starter into packages/create-modular-monolith/template
4
- * for publishing with the npm CLI.
5
- */
6
- import { cpSync, existsSync, mkdirSync, rmSync } from "fs";
7
- import { join } from "path";
8
-
9
- const root = new URL("../", import.meta.url).pathname;
10
- const target = join(root, "packages/create-modular-monolith/template");
11
-
12
- const COPY_ROOTS = ["backend", "frontend", "docs", "scripts", "README.md", ".gitignore", "package.json"];
13
-
14
- const EXCLUDE_DIRS = new Set([
15
- "node_modules",
16
- ".git",
17
- "dist",
18
- "coverage",
19
- "packages"
20
- ]);
21
-
22
- if (existsSync(target)) {
23
- rmSync(target, { recursive: true, force: true });
24
- }
25
- mkdirSync(target, { recursive: true });
26
-
27
- for (const item of COPY_ROOTS) {
28
- const src = join(root, item);
29
- if (!existsSync(src)) {
30
- console.warn(`Skip missing: ${item}`);
31
- continue;
32
- }
33
- const dest = join(target, item);
34
- cpSync(src, dest, {
35
- recursive: true,
36
- filter: (sourcePath) => {
37
- const parts = sourcePath.split(/[/\\]/);
38
- return !parts.some((part) => EXCLUDE_DIRS.has(part));
39
- }
40
- });
41
- console.log(`✓ ${item}`);
42
- }
43
-
44
- console.log(`\nTemplate synced to ${target}`);