create-project-arch 1.2.0 → 1.3.1

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2026-03-08
9
+
10
+ ### Added
11
+
12
+ - Foundation, domain, architecture spec, concept-map, decision record, and gap-closure scaffolds.
13
+ - Local validation hook scaffold (`scripts/validate.sh` and `.githooks/pre-commit`).
14
+
15
+ ### Changed
16
+
17
+ - Validation-hook copy behavior now merges directories and preserves existing files while adding missing template files.
18
+ - Template scaffolding flow expanded to include architecture governance docs by default.
19
+
8
20
  ## [1.1.0] - 2026-03-07
9
21
 
10
22
  ### Added
@@ -39,5 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
39
51
  - Template customization options
40
52
  - Force mode for non-empty directories
41
53
 
42
- [1.1.0]: https://github.com/project-arch/project-arch-system/compare/v1.0.0...v1.1.0
43
- [1.0.0]: https://github.com/project-arch/project-arch-system/releases/tag/v1.0.0
54
+ [1.1.0]: https://github.com/MissTitanK3/project-arch-system/compare/v1.0.0...v1.1.0
55
+ [1.0.0]: https://github.com/MissTitanK3/project-arch-system/releases/tag/v1.0.0
56
+ [1.3.0]: https://github.com/MissTitanK3/project-arch-system/compare/v1.1.0...v1.3.0
package/README.md CHANGED
@@ -80,7 +80,7 @@ When you run `create-project-arch`, it:
80
80
 
81
81
  ### Project Structure
82
82
 
83
- ```
83
+ ```bash
84
84
  my-awesome-project/
85
85
  ├── apps/
86
86
  │ ├── arch/ # Architecture UI (Next.js app)
@@ -107,12 +107,138 @@ my-awesome-project/
107
107
  │ │ └── phase-1/
108
108
  │ │ └── milestones/
109
109
  │ ├── decisions/
110
- └── docs/
110
+ ├── docs/
111
+ │ └── concept-map.json # Concept-to-module traceability map
112
+ ├── architecture/
113
+ │ ├── foundation/ # Milestone 1 prerequisite docs
114
+ │ │ ├── prompt.md
115
+ │ │ ├── project-overview.md
116
+ │ │ ├── goals.md
117
+ │ │ ├── user-journey.md
118
+ │ │ └── scope.md
119
+ │ ├── architecture/ # System architecture specs
120
+ │ │ ├── SPEC_TEMPLATE.md
121
+ │ │ └── example-system.md
122
+ │ ├── decisions/ # Architecture decision records
123
+ │ ├── DECISION_TEMPLATE.md
124
+ │ └── example-decision.md
125
+ │ └── reference/ # Reusable quality and closure references
126
+ │ ├── GAP_CLOSURE_TEMPLATE.md
127
+ │ └── example-gap-closure.md
128
+ ├── arch-domains/ # Domain boundaries and ownership
129
+ │ ├── README.md
130
+ │ ├── domains.json
131
+ │ ├── DOMAIN_TEMPLATE.md
132
+ │ ├── core.md
133
+ │ ├── ui.md
134
+ │ └── api.md
135
+ ├── scripts/
136
+ │ └── validate.sh # Local architecture validation hook
137
+ ├── .githooks/
138
+ │ └── pre-commit # Optional local pre-commit validation hook
111
139
  ├── package.json
112
140
  ├── turbo.json
113
141
  └── pnpm-workspace.yaml
114
142
  ```
115
143
 
144
+ ### Milestone 1 Prerequisites
145
+
146
+ Scaffolded projects include these foundation docs by default under `architecture/foundation/`:
147
+
148
+ - `prompt.md` (canonical source brief)
149
+ - `project-overview.md`
150
+ - `goals.md`
151
+ - `user-journey.md`
152
+ - `scope.md`
153
+
154
+ Complete these files first before implementing milestone tasks.
155
+
156
+ ### Domain Spec Scaffold
157
+
158
+ Scaffolded projects also include baseline domain specs under `arch-domains/`:
159
+
160
+ - `domains.json` with starter domains (`core`, `ui`, `api`)
161
+ - `DOMAIN_TEMPLATE.md` with required sections:
162
+ - Responsibilities
163
+ - Primary Data Ownership
164
+ - Interface Contracts
165
+ - Non-Goals
166
+ - Milestone Mapping
167
+ - Starter specs: `core.md`, `ui.md`, and `api.md`
168
+
169
+ ### System Architecture Spec Scaffold
170
+
171
+ Scaffolded projects include reusable architecture specs under `architecture/architecture/`:
172
+
173
+ - `SPEC_TEMPLATE.md` with required sections:
174
+ - Purpose
175
+ - Scope (in-scope / out-of-scope)
176
+ - Key Definitions
177
+ - Design
178
+ - Data Model
179
+ - Owning Domain
180
+ - MVP Constraints
181
+ - `example-system.md` showing a realistic completed reference
182
+
183
+ ### Concept-To-Module Traceability Scaffold
184
+
185
+ Scaffolded projects include `arch-model/concept-map.json` with:
186
+
187
+ - concept metadata (`id`, `name`, `description`)
188
+ - owning domain assignment
189
+ - module responsibilities
190
+ - implementation surfaces (API/UI/component/code paths)
191
+ - concept dependencies
192
+ - domain-module mapping and implementation checklist placeholders
193
+
194
+ ### Decision Record Scaffold
195
+
196
+ Scaffolded projects include architecture decision templates under `architecture/decisions/`:
197
+
198
+ - `DECISION_TEMPLATE.md` with structured frontmatter (`id`, `title`, `slug`, `status`, timestamps, `relatedTasks`, `relatedDocs`, `supersedes`)
199
+ - Required sections:
200
+ - Context
201
+ - Decision
202
+ - Rationale
203
+ - Alternatives Considered
204
+ - Affected Artifacts
205
+ - Implementation Status Checklist
206
+ - `example-decision.md` demonstrating a completed decision record
207
+
208
+ Use `pa decision new` for operational decision creation linked into roadmap decision indexes.
209
+
210
+ ### Milestone Gap-Closure Report Scaffold
211
+
212
+ Scaffolded projects include closure report artifacts under `architecture/reference/`:
213
+
214
+ - `GAP_CLOSURE_TEMPLATE.md` with sections for:
215
+ - Executive Summary
216
+ - Gap Categories And Resolutions
217
+ - Layer Synchronization Check
218
+ - Coverage Audit
219
+ - Remaining Gaps And Follow-On Items
220
+ - Template Improvement Feedback
221
+ - `example-gap-closure.md` demonstrating a completed closure report
222
+
223
+ Recommended workflow:
224
+
225
+ 1. Complete milestone tasks and decision updates.
226
+ 2. Run `pa check` and `pa report`.
227
+ 3. Record closure outcomes in milestone closure report.
228
+ 4. Track remaining gaps as follow-on tasks/decisions.
229
+
230
+ ### Local Validation Hook Scaffold
231
+
232
+ Scaffolded projects include local validation automation assets:
233
+
234
+ - `scripts/validate.sh` runs:
235
+ - `pnpm arch:check`
236
+ - `pnpm arch:report`
237
+ - Optional local hook example:
238
+ - `.githooks/pre-commit`
239
+
240
+ Use these hooks to keep architecture validation consistent in local workflows.
241
+
116
242
  ## Available Templates
117
243
 
118
244
  ### arch-ui (Default)
@@ -318,7 +444,7 @@ node dist/cli.js test-output --force
318
444
 
319
445
  Templates are stored in `templates/` directory:
320
446
 
321
- ```
447
+ ```bash
322
448
  templates/
323
449
  ├── arch-ui/
324
450
  │ ├── package.json
@@ -472,19 +598,19 @@ npm run dev
472
598
 
473
599
  To add project-arch to an existing project:
474
600
 
475
- 1. Install project-arch:
601
+ ### 1. Install project-arch
476
602
 
477
603
  ```bash
478
604
  pnpm add project-arch -w
479
605
  ```
480
606
 
481
- 2. Initialize architecture:
607
+ ### 2. Initialize architecture
482
608
 
483
609
  ```bash
484
610
  pnpm exec pa init
485
611
  ```
486
612
 
487
- 3. (Optional) Copy template files manually from this repository
613
+ ### 3. (Optional) Copy template files manually from this repository
488
614
 
489
615
  ## API Reference (Programmatic Usage)
490
616
 
@@ -530,9 +656,9 @@ MIT License - see [LICENSE](LICENSE) for details.
530
656
 
531
657
  ## Support
532
658
 
533
- - 📖 [Documentation](https://github.com/project-arch/project-arch-system#readme)
534
- - 🐛 [Issue Tracker](https://github.com/project-arch/project-arch-system/issues)
535
- - 💬 [Discussions](https://github.com/project-arch/project-arch-system/discussions)
659
+ - 📖 [Documentation](https://github.com/MissTitanK3/project-arch-system#readme)
660
+ - 🐛 [Issue Tracker](https://github.com/MissTitanK3/project-arch-system/issues)
661
+ - 💬 [Discussions](https://github.com/MissTitanK3/project-arch-system/discussions)
536
662
 
537
663
  ## Acknowledgments
538
664
 
@@ -540,7 +666,7 @@ Built with:
540
666
 
541
667
  - [Commander.js](https://github.com/tj/commander.js) - CLI framework
542
668
  - [fs-extra](https://github.com/jprichardson/node-fs-extra) - File system utilities
543
- - [project-arch](https://github.com/project-arch/project-arch-system) - Architecture management
669
+ - [project-arch](https://github.com/MissTitanK3/project-arch-system) - Architecture management
544
670
 
545
671
  ---
546
672
 
package/dist/cli.js CHANGED
@@ -135,6 +135,150 @@ async function scaffoldArchitectureApps(targetDir) {
135
135
  await fs_extra_1.default.writeJSON(archUiPkgPath, nextArchUiPkg, { spaces: 2 });
136
136
  await fs_extra_1.default.appendFile(archUiPkgPath, "\n");
137
137
  }
138
+ async function scaffoldFoundationDocs(targetDir) {
139
+ const templatesRoot = getTemplatesRoot();
140
+ const foundationTemplateRoot = path_1.default.join(templatesRoot, "foundation");
141
+ const foundationTargetRoot = path_1.default.join(targetDir, "architecture", "foundation");
142
+ if (!(await fs_extra_1.default.pathExists(foundationTemplateRoot))) {
143
+ throw new Error(`Missing foundation templates at ${foundationTemplateRoot}`);
144
+ }
145
+ await fs_extra_1.default.ensureDir(foundationTargetRoot);
146
+ const foundationEntries = await fs_extra_1.default.readdir(foundationTemplateRoot);
147
+ for (const entry of foundationEntries) {
148
+ const sourcePath = path_1.default.join(foundationTemplateRoot, entry);
149
+ const targetPath = path_1.default.join(foundationTargetRoot, entry);
150
+ if (await fs_extra_1.default.pathExists(targetPath)) {
151
+ continue;
152
+ }
153
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
154
+ }
155
+ }
156
+ async function scaffoldDomainSpecs(targetDir) {
157
+ const templatesRoot = getTemplatesRoot();
158
+ const domainsTemplateRoot = path_1.default.join(templatesRoot, "domains");
159
+ const domainsTargetRoot = path_1.default.join(targetDir, "arch-domains");
160
+ if (!(await fs_extra_1.default.pathExists(domainsTemplateRoot))) {
161
+ throw new Error(`Missing domain templates at ${domainsTemplateRoot}`);
162
+ }
163
+ await fs_extra_1.default.ensureDir(domainsTargetRoot);
164
+ const domainEntries = await fs_extra_1.default.readdir(domainsTemplateRoot);
165
+ for (const entry of domainEntries) {
166
+ const sourcePath = path_1.default.join(domainsTemplateRoot, entry);
167
+ const targetPath = path_1.default.join(domainsTargetRoot, entry);
168
+ if (entry === "DOMAIN_TEMPLATE.md" || entry === "README.md") {
169
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: true });
170
+ continue;
171
+ }
172
+ if (entry === "domains.json") {
173
+ if (await fs_extra_1.default.pathExists(targetPath)) {
174
+ const existing = (await fs_extra_1.default.readJSON(targetPath));
175
+ if (Array.isArray(existing.domains) && existing.domains.length > 0) {
176
+ continue;
177
+ }
178
+ }
179
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: true });
180
+ continue;
181
+ }
182
+ if (await fs_extra_1.default.pathExists(targetPath)) {
183
+ continue;
184
+ }
185
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
186
+ }
187
+ }
188
+ async function scaffoldArchitectureSpecs(targetDir) {
189
+ const templatesRoot = getTemplatesRoot();
190
+ const architectureSpecsTemplateRoot = path_1.default.join(templatesRoot, "architecture-specs");
191
+ const architectureSpecsTargetRoot = path_1.default.join(targetDir, "architecture", "architecture");
192
+ if (!(await fs_extra_1.default.pathExists(architectureSpecsTemplateRoot))) {
193
+ throw new Error(`Missing architecture spec templates at ${architectureSpecsTemplateRoot}`);
194
+ }
195
+ await fs_extra_1.default.ensureDir(architectureSpecsTargetRoot);
196
+ const specEntries = await fs_extra_1.default.readdir(architectureSpecsTemplateRoot);
197
+ for (const entry of specEntries) {
198
+ const sourcePath = path_1.default.join(architectureSpecsTemplateRoot, entry);
199
+ const targetPath = path_1.default.join(architectureSpecsTargetRoot, entry);
200
+ if (await fs_extra_1.default.pathExists(targetPath)) {
201
+ continue;
202
+ }
203
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
204
+ }
205
+ }
206
+ async function scaffoldConceptMap(targetDir) {
207
+ const templatesRoot = getTemplatesRoot();
208
+ const conceptMapTemplatePath = path_1.default.join(templatesRoot, "concept-map", "concept-map.json");
209
+ const conceptMapTargetPath = path_1.default.join(targetDir, "arch-model", "concept-map.json");
210
+ if (!(await fs_extra_1.default.pathExists(conceptMapTemplatePath))) {
211
+ throw new Error(`Missing concept-map template at ${conceptMapTemplatePath}`);
212
+ }
213
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(conceptMapTargetPath));
214
+ if (await fs_extra_1.default.pathExists(conceptMapTargetPath)) {
215
+ return;
216
+ }
217
+ await fs_extra_1.default.copy(conceptMapTemplatePath, conceptMapTargetPath, { overwrite: false });
218
+ }
219
+ async function scaffoldDecisionRecords(targetDir) {
220
+ const templatesRoot = getTemplatesRoot();
221
+ const decisionsTemplateRoot = path_1.default.join(templatesRoot, "decisions");
222
+ const decisionsTargetRoot = path_1.default.join(targetDir, "architecture", "decisions");
223
+ if (!(await fs_extra_1.default.pathExists(decisionsTemplateRoot))) {
224
+ throw new Error(`Missing decision templates at ${decisionsTemplateRoot}`);
225
+ }
226
+ await fs_extra_1.default.ensureDir(decisionsTargetRoot);
227
+ const entries = await fs_extra_1.default.readdir(decisionsTemplateRoot);
228
+ for (const entry of entries) {
229
+ const sourcePath = path_1.default.join(decisionsTemplateRoot, entry);
230
+ const targetPath = path_1.default.join(decisionsTargetRoot, entry);
231
+ if (await fs_extra_1.default.pathExists(targetPath)) {
232
+ continue;
233
+ }
234
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
235
+ }
236
+ }
237
+ async function scaffoldGapClosureTemplates(targetDir) {
238
+ const templatesRoot = getTemplatesRoot();
239
+ const gapClosureTemplateRoot = path_1.default.join(templatesRoot, "gap-closure");
240
+ const gapClosureTargetRoot = path_1.default.join(targetDir, "architecture", "reference");
241
+ if (!(await fs_extra_1.default.pathExists(gapClosureTemplateRoot))) {
242
+ throw new Error(`Missing gap-closure templates at ${gapClosureTemplateRoot}`);
243
+ }
244
+ await fs_extra_1.default.ensureDir(gapClosureTargetRoot);
245
+ const entries = await fs_extra_1.default.readdir(gapClosureTemplateRoot);
246
+ for (const entry of entries) {
247
+ const sourcePath = path_1.default.join(gapClosureTemplateRoot, entry);
248
+ const targetPath = path_1.default.join(gapClosureTargetRoot, entry);
249
+ if (await fs_extra_1.default.pathExists(targetPath)) {
250
+ continue;
251
+ }
252
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
253
+ }
254
+ }
255
+ async function scaffoldValidationHooks(targetDir) {
256
+ const templatesRoot = getTemplatesRoot();
257
+ const validationTemplateRoot = path_1.default.join(templatesRoot, "validation-hooks");
258
+ if (!(await fs_extra_1.default.pathExists(validationTemplateRoot))) {
259
+ throw new Error(`Missing validation hook templates at ${validationTemplateRoot}`);
260
+ }
261
+ const entries = await fs_extra_1.default.readdir(validationTemplateRoot);
262
+ for (const entry of entries) {
263
+ const sourcePath = path_1.default.join(validationTemplateRoot, entry);
264
+ const targetPath = path_1.default.join(targetDir, entry);
265
+ await copyMissingEntries(sourcePath, targetPath);
266
+ }
267
+ }
268
+ async function copyMissingEntries(sourcePath, targetPath) {
269
+ const sourceStats = await fs_extra_1.default.stat(sourcePath);
270
+ if (!sourceStats.isDirectory()) {
271
+ if (!(await fs_extra_1.default.pathExists(targetPath))) {
272
+ await fs_extra_1.default.copy(sourcePath, targetPath, { overwrite: false });
273
+ }
274
+ return;
275
+ }
276
+ await fs_extra_1.default.ensureDir(targetPath);
277
+ const children = await fs_extra_1.default.readdir(sourcePath);
278
+ for (const child of children) {
279
+ await copyMissingEntries(path_1.default.join(sourcePath, child), path_1.default.join(targetPath, child));
280
+ }
281
+ }
138
282
  async function upsertArchModulesInMap(targetDir) {
139
283
  const modulesPath = path_1.default.join(targetDir, "arch-model", "modules.json");
140
284
  if (!(await fs_extra_1.default.pathExists(modulesPath))) {
@@ -213,6 +357,13 @@ async function main() {
213
357
  }
214
358
  }
215
359
  await runPaInit(targetDir, options);
360
+ await scaffoldFoundationDocs(targetDir);
361
+ await scaffoldDomainSpecs(targetDir);
362
+ await scaffoldArchitectureSpecs(targetDir);
363
+ await scaffoldConceptMap(targetDir);
364
+ await scaffoldDecisionRecords(targetDir);
365
+ await scaffoldGapClosureTemplates(targetDir);
366
+ await scaffoldValidationHooks(targetDir);
216
367
  await scaffoldArchitectureApps(targetDir);
217
368
  await upsertArchModulesInMap(targetDir);
218
369
  await wireProjectArchUsage(targetDir);
package/dist/cli.test.js CHANGED
@@ -1,8 +1,199 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
3
8
  const vitest_1 = require("vitest");
9
+ const currentDir = __dirname;
10
+ const archUiEslintConfigPath = node_path_1.default.resolve(currentDir, "../templates/arch-ui/eslint.config.js");
11
+ const foundationTemplateDir = node_path_1.default.resolve(currentDir, "../templates/foundation");
12
+ const domainTemplateDir = node_path_1.default.resolve(currentDir, "../templates/domains");
13
+ const architectureSpecTemplateDir = node_path_1.default.resolve(currentDir, "../templates/architecture-specs");
14
+ const conceptMapTemplatePath = node_path_1.default.resolve(currentDir, "../templates/concept-map/concept-map.json");
15
+ const decisionTemplateDir = node_path_1.default.resolve(currentDir, "../templates/decisions");
16
+ const gapClosureTemplateDir = node_path_1.default.resolve(currentDir, "../templates/gap-closure");
17
+ const validationHookTemplateDir = node_path_1.default.resolve(currentDir, "../templates/validation-hooks");
18
+ const foundationTemplateExpectations = [
19
+ { fileName: "prompt.md", sectionHeader: "## Source Prompt" },
20
+ { fileName: "project-overview.md", sectionHeader: "## Problem Statement" },
21
+ { fileName: "goals.md", sectionHeader: "## Primary Goals" },
22
+ { fileName: "user-journey.md", sectionHeader: "## Journey Steps" },
23
+ { fileName: "scope.md", sectionHeader: "## In Scope" },
24
+ ];
25
+ const domainTemplateExpectations = [
26
+ { fileName: "core.md", sectionHeader: "## Responsibilities" },
27
+ { fileName: "ui.md", sectionHeader: "## Responsibilities" },
28
+ { fileName: "api.md", sectionHeader: "## Responsibilities" },
29
+ ];
30
+ const architectureSpecTemplateSections = [
31
+ "## Purpose",
32
+ "## Scope",
33
+ "## Key Definitions",
34
+ "## Design",
35
+ "## Data Model",
36
+ "## Owning Domain",
37
+ "## MVP Constraints",
38
+ ];
39
+ const decisionTemplateSections = [
40
+ "## Context",
41
+ "## Decision",
42
+ "## Rationale",
43
+ "## Alternatives Considered",
44
+ "## Affected Artifacts",
45
+ "## Implementation Status Checklist",
46
+ ];
47
+ const gapClosureTemplateSections = [
48
+ "## Executive Summary",
49
+ "## Gap Categories And Resolutions",
50
+ "## Layer Synchronization Check",
51
+ "## Coverage Audit",
52
+ "## Remaining Gaps And Follow-On Items",
53
+ "## Template Improvement Feedback",
54
+ ];
55
+ function getImportedNextJsSymbol(source) {
56
+ const match = source.match(/import\s+\{\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*\}\s+from\s+["']@repo\/eslint-config\/next-js["']/);
57
+ return match?.[1] ?? null;
58
+ }
59
+ function getDefaultExportedSymbol(source) {
60
+ const match = source.match(/export\s+default\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*;?/);
61
+ return match?.[1] ?? null;
62
+ }
4
63
  (0, vitest_1.describe)("create-project-arch CLI", () => {
5
64
  (0, vitest_1.it)("runs test suite successfully", () => {
6
65
  (0, vitest_1.expect)(true).toBe(true);
7
66
  });
67
+ (0, vitest_1.it)("keeps arch-ui eslint template wired to @repo/eslint-config/next-js export", () => {
68
+ const archUiConfigSource = node_fs_1.default.readFileSync(archUiEslintConfigPath, "utf8");
69
+ const importedSymbol = getImportedNextJsSymbol(archUiConfigSource);
70
+ const defaultExportedSymbol = getDefaultExportedSymbol(archUiConfigSource);
71
+ (0, vitest_1.expect)(importedSymbol).toBeTruthy();
72
+ (0, vitest_1.expect)(defaultExportedSymbol).toBeTruthy();
73
+ (0, vitest_1.expect)(defaultExportedSymbol).toBe(importedSymbol);
74
+ (0, vitest_1.expect)(importedSymbol).toBe("nextJsConfig");
75
+ });
76
+ (0, vitest_1.it)("includes all foundation document templates with structured guidance", () => {
77
+ for (const expectation of foundationTemplateExpectations) {
78
+ const templatePath = node_path_1.default.join(foundationTemplateDir, expectation.fileName);
79
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(templatePath)).toBe(true);
80
+ const templateSource = node_fs_1.default.readFileSync(templatePath, "utf8");
81
+ (0, vitest_1.expect)(templateSource).toContain(expectation.sectionHeader);
82
+ (0, vitest_1.expect)(templateSource).toContain("<!-- Guidance:");
83
+ }
84
+ });
85
+ (0, vitest_1.it)("includes domain spec templates with ownership and milestone mapping guidance", () => {
86
+ const domainsJsonPath = node_path_1.default.join(domainTemplateDir, "domains.json");
87
+ const domainTemplatePath = node_path_1.default.join(domainTemplateDir, "DOMAIN_TEMPLATE.md");
88
+ const domainReadmePath = node_path_1.default.join(domainTemplateDir, "README.md");
89
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(domainReadmePath)).toBe(true);
90
+ (0, vitest_1.expect)(node_fs_1.default.readFileSync(domainReadmePath, "utf8")).toContain("domain boundaries");
91
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(domainsJsonPath)).toBe(true);
92
+ const domainsJson = JSON.parse(node_fs_1.default.readFileSync(domainsJsonPath, "utf8"));
93
+ (0, vitest_1.expect)(Array.isArray(domainsJson.domains)).toBe(true);
94
+ (0, vitest_1.expect)(domainsJson.domains?.map((domain) => domain.name)).toEqual(["core", "ui", "api"]);
95
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(domainTemplatePath)).toBe(true);
96
+ const domainTemplateSource = node_fs_1.default.readFileSync(domainTemplatePath, "utf8");
97
+ (0, vitest_1.expect)(domainTemplateSource).toContain("## Responsibilities");
98
+ (0, vitest_1.expect)(domainTemplateSource).toContain("## Primary Data Ownership");
99
+ (0, vitest_1.expect)(domainTemplateSource).toContain("## Interface Contracts");
100
+ (0, vitest_1.expect)(domainTemplateSource).toContain("## Non-Goals");
101
+ (0, vitest_1.expect)(domainTemplateSource).toContain("## Milestone Mapping");
102
+ for (const expectation of domainTemplateExpectations) {
103
+ const templatePath = node_path_1.default.join(domainTemplateDir, expectation.fileName);
104
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(templatePath)).toBe(true);
105
+ const templateSource = node_fs_1.default.readFileSync(templatePath, "utf8");
106
+ (0, vitest_1.expect)(templateSource).toContain(expectation.sectionHeader);
107
+ (0, vitest_1.expect)(templateSource).toContain("## Milestone Mapping");
108
+ }
109
+ });
110
+ (0, vitest_1.it)("includes reusable architecture spec template and example", () => {
111
+ const specTemplatePath = node_path_1.default.join(architectureSpecTemplateDir, "SPEC_TEMPLATE.md");
112
+ const exampleSpecPath = node_path_1.default.join(architectureSpecTemplateDir, "example-system.md");
113
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(specTemplatePath)).toBe(true);
114
+ const specTemplateSource = node_fs_1.default.readFileSync(specTemplatePath, "utf8");
115
+ for (const section of architectureSpecTemplateSections) {
116
+ (0, vitest_1.expect)(specTemplateSource).toContain(section);
117
+ }
118
+ (0, vitest_1.expect)(specTemplateSource).toContain("<!-- Guidance:");
119
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(exampleSpecPath)).toBe(true);
120
+ const exampleSpecSource = node_fs_1.default.readFileSync(exampleSpecPath, "utf8");
121
+ (0, vitest_1.expect)(exampleSpecSource).toContain("# Example System Specification");
122
+ (0, vitest_1.expect)(exampleSpecSource).toContain("## Purpose");
123
+ (0, vitest_1.expect)(exampleSpecSource).toContain("## MVP Constraints");
124
+ });
125
+ (0, vitest_1.it)("includes concept-map template with traceability schema placeholders", () => {
126
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(conceptMapTemplatePath)).toBe(true);
127
+ const conceptMap = JSON.parse(node_fs_1.default.readFileSync(conceptMapTemplatePath, "utf8"));
128
+ (0, vitest_1.expect)(conceptMap.schemaVersion).toBe("1.0");
129
+ (0, vitest_1.expect)(Array.isArray(conceptMap.concepts)).toBe(true);
130
+ (0, vitest_1.expect)((conceptMap.concepts ?? []).length).toBeGreaterThanOrEqual(2);
131
+ const firstConcept = conceptMap.concepts?.[0] ?? {};
132
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("id");
133
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("name");
134
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("description");
135
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("owningDomain");
136
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("moduleResponsibilities");
137
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("implementationSurfaces");
138
+ (0, vitest_1.expect)(firstConcept).toHaveProperty("dependencies");
139
+ (0, vitest_1.expect)(Array.isArray(conceptMap.domainModuleMapping)).toBe(true);
140
+ (0, vitest_1.expect)(Array.isArray(conceptMap.implementationChecklist)).toBe(true);
141
+ });
142
+ (0, vitest_1.it)("includes decision record template and example with structured frontmatter", () => {
143
+ const decisionTemplatePath = node_path_1.default.join(decisionTemplateDir, "DECISION_TEMPLATE.md");
144
+ const decisionExamplePath = node_path_1.default.join(decisionTemplateDir, "example-decision.md");
145
+ const decisionReadmePath = node_path_1.default.join(decisionTemplateDir, "README.md");
146
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(decisionReadmePath)).toBe(true);
147
+ (0, vitest_1.expect)(node_fs_1.default.readFileSync(decisionReadmePath, "utf8")).toContain("pa decision new");
148
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(decisionTemplatePath)).toBe(true);
149
+ const decisionTemplateSource = node_fs_1.default.readFileSync(decisionTemplatePath, "utf8");
150
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("---");
151
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("id:");
152
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("title:");
153
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("slug:");
154
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("status:");
155
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("createdAt:");
156
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("updatedAt:");
157
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("relatedTasks:");
158
+ (0, vitest_1.expect)(decisionTemplateSource).toContain("relatedDocs:");
159
+ for (const section of decisionTemplateSections) {
160
+ (0, vitest_1.expect)(decisionTemplateSource).toContain(section);
161
+ }
162
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(decisionExamplePath)).toBe(true);
163
+ const decisionExampleSource = node_fs_1.default.readFileSync(decisionExamplePath, "utf8");
164
+ (0, vitest_1.expect)(decisionExampleSource).toContain('status: "accepted"');
165
+ (0, vitest_1.expect)(decisionExampleSource).toContain("## Decision");
166
+ (0, vitest_1.expect)(decisionExampleSource).toContain("## Alternatives Considered");
167
+ });
168
+ (0, vitest_1.it)("includes milestone gap-closure report template and example", () => {
169
+ const gapClosureTemplatePath = node_path_1.default.join(gapClosureTemplateDir, "GAP_CLOSURE_TEMPLATE.md");
170
+ const gapClosureExamplePath = node_path_1.default.join(gapClosureTemplateDir, "example-gap-closure.md");
171
+ const gapClosureReadmePath = node_path_1.default.join(gapClosureTemplateDir, "README.md");
172
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(gapClosureReadmePath)).toBe(true);
173
+ (0, vitest_1.expect)(node_fs_1.default.readFileSync(gapClosureReadmePath, "utf8")).toContain("pa check");
174
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(gapClosureTemplatePath)).toBe(true);
175
+ const gapClosureTemplateSource = node_fs_1.default.readFileSync(gapClosureTemplatePath, "utf8");
176
+ for (const section of gapClosureTemplateSections) {
177
+ (0, vitest_1.expect)(gapClosureTemplateSource).toContain(section);
178
+ }
179
+ (0, vitest_1.expect)(gapClosureTemplateSource).toContain("- [ ]");
180
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(gapClosureExamplePath)).toBe(true);
181
+ const gapClosureExampleSource = node_fs_1.default.readFileSync(gapClosureExamplePath, "utf8");
182
+ (0, vitest_1.expect)(gapClosureExampleSource).toContain("# Milestone Gap-Closure Report - Example");
183
+ (0, vitest_1.expect)(gapClosureExampleSource).toContain("## Layer Synchronization Check");
184
+ (0, vitest_1.expect)(gapClosureExampleSource).toContain("- [x]");
185
+ });
186
+ (0, vitest_1.it)("includes local validation hook script and pre-commit example", () => {
187
+ const validateScriptPath = node_path_1.default.join(validationHookTemplateDir, "scripts", "validate.sh");
188
+ const preCommitPath = node_path_1.default.join(validationHookTemplateDir, ".githooks", "pre-commit");
189
+ const readmePath = node_path_1.default.join(validationHookTemplateDir, "README.md");
190
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(validateScriptPath)).toBe(true);
191
+ const validateScriptSource = node_fs_1.default.readFileSync(validateScriptPath, "utf8");
192
+ (0, vitest_1.expect)(validateScriptSource).toContain("pnpm arch:check");
193
+ (0, vitest_1.expect)(validateScriptSource).toContain("pnpm arch:report");
194
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(preCommitPath)).toBe(true);
195
+ (0, vitest_1.expect)(node_fs_1.default.readFileSync(preCommitPath, "utf8")).toContain("sh scripts/validate.sh");
196
+ (0, vitest_1.expect)(node_fs_1.default.existsSync(readmePath)).toBe(true);
197
+ (0, vitest_1.expect)(node_fs_1.default.readFileSync(readmePath, "utf8")).toContain("Task Verification Guidance");
198
+ });
8
199
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-project-arch",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Scaffold new projects with Project Arch templates including Next.js apps and component libraries",
5
5
  "keywords": [
6
6
  "create",
@@ -12,13 +12,13 @@
12
12
  "turborepo",
13
13
  "monorepo"
14
14
  ],
15
- "homepage": "https://github.com/project-arch/project-arch-system#readme",
15
+ "homepage": "https://github.com/MissTitanK3/project-arch-system#readme",
16
16
  "bugs": {
17
- "url": "https://github.com/project-arch/project-arch-system/issues"
17
+ "url": "https://github.com/MissTitanK3/project-arch-system/issues"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "git+https://github.com/project-arch/project-arch-system.git",
21
+ "url": "git+https://github.com/MissTitanK3/project-arch-system.git",
22
22
  "directory": "packages/create-project-arch"
23
23
  },
24
24
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "dependencies": {
38
38
  "commander": "^12.1.0",
39
39
  "fs-extra": "^11.3.0",
40
- "project-arch": "^1.0.0"
40
+ "project-arch": "^1.3.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/fs-extra": "^11.0.4",
@@ -1,2 +1,2 @@
1
- import { config } from '@repo/eslint-config/next-js';
2
- export default config;
1
+ import { nextJsConfig } from '@repo/eslint-config/next-js';
2
+ export default nextJsConfig;