claudeos-core 1.3.0 → 1.4.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.

Potentially problematic release.


This version of claudeos-core might be problematic. Click here for more details.

Files changed (34) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.de.md +75 -27
  3. package/README.es.md +75 -27
  4. package/README.fr.md +75 -27
  5. package/README.hi.md +76 -28
  6. package/README.ja.md +94 -28
  7. package/README.ko.md +94 -28
  8. package/README.md +93 -26
  9. package/README.ru.md +75 -27
  10. package/README.vi.md +75 -27
  11. package/README.zh-CN.md +76 -28
  12. package/bin/cli.js +38 -10
  13. package/bootstrap.sh +13 -2
  14. package/content-validator/index.js +18 -13
  15. package/manifest-generator/index.js +24 -1
  16. package/package.json +1 -1
  17. package/pass-prompts/templates/java-spring/pass1.md +1 -1
  18. package/pass-prompts/templates/java-spring/pass3.md +25 -5
  19. package/pass-prompts/templates/kotlin-spring/pass1.md +1 -1
  20. package/pass-prompts/templates/kotlin-spring/pass3.md +25 -5
  21. package/pass-prompts/templates/node-express/pass1.md +1 -1
  22. package/pass-prompts/templates/node-express/pass3.md +24 -5
  23. package/pass-prompts/templates/node-nextjs/pass1.md +1 -1
  24. package/pass-prompts/templates/node-nextjs/pass3.md +25 -5
  25. package/pass-prompts/templates/python-django/pass1.md +1 -1
  26. package/pass-prompts/templates/python-django/pass3.md +24 -5
  27. package/pass-prompts/templates/python-fastapi/pass1.md +1 -1
  28. package/pass-prompts/templates/python-fastapi/pass3.md +24 -5
  29. package/plan-installer/domain-grouper.js +3 -10
  30. package/plan-installer/prompt-generator.js +0 -5
  31. package/plan-installer/stack-detector.js +2 -2
  32. package/plan-installer/structure-scanner.js +58 -7
  33. package/plan-validator/index.js +4 -2
  34. package/sync-checker/index.js +4 -2
@@ -17,6 +17,13 @@ If a standard defines a specific pattern (e.g., import path, file naming, API us
17
17
  the corresponding rule MUST use the same pattern. Before generating each rule file,
18
18
  verify it is consistent with the related standard file.
19
19
 
20
+ CRITICAL — Code Example Accuracy:
21
+ ALL code examples in rules and standards MUST use EXACT method names, class names,
22
+ and signatures from pass2-merged.json analysis data.
23
+ Do NOT paraphrase, rename, or infer API names.
24
+ If a method signature is not captured in the analysis data,
25
+ write "See corresponding standard for exact API" instead of guessing.
26
+
20
27
  CRITICAL — CLAUDE.md Reference Table Completeness:
21
28
  The reference table in CLAUDE.md MUST list ALL generated standard files, not just core.
22
29
  Include all frontend-ui, backend-api, security-db, infra, and verification standards.
@@ -55,19 +62,32 @@ Generation targets:
55
62
  - Key rules summary table
56
63
 
57
64
  3. .claude/rules/ (active domains only)
58
- - Every rule file MUST include `paths: ["**/*"]` in YAML frontmatter — this ensures Claude Code always loads the rule regardless of which file is being edited
59
65
  - Write the full rule content directly in each file (self-contained, no external references)
60
66
  - Include 5-15 lines of key rules with concrete examples
61
67
  - Do NOT use @import — it is not a Claude Code feature and does not work
62
- - MUST generate `.claude/rules/00.core/00.standard-reference.md` a mandatory rule file that instructs Claude Code to Read the standard documents before coding. Structure it as:
68
+ - Each rule file MUST end with a `## Reference` section linking to the corresponding standard file(s):
69
+ ```
70
+ ## Reference
71
+ > For detailed patterns and examples, Read: claudeos-core/standard/20.frontend-ui/01.component-patterns.md
72
+ ```
73
+ - `paths:` frontmatter per rule category:
74
+ - `00.core/*` rules: `paths: ["**/*"]` — always loaded (architecture, naming are universally needed)
75
+ - `10.backend/*` rules: `paths: ["**/*"]` — always loaded (backend rules needed for any source editing)
76
+ - `20.frontend/*` rules: `paths: ["**/*"]` — always loaded (frontend rules needed for any source editing)
77
+ - `30.security-db/*` rules: `paths: ["**/*"]` — always loaded (cross-cutting concerns)
78
+ - `40.infra/*` rules: `paths: ["**/*.json", "**/*.env*", "**/next.config.*", "**/Dockerfile*", "**/*.yml", "**/*.yaml"]` — loaded only for config/infra files
79
+ - `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]` — loaded only when editing claudeos-core files
80
+ - MUST generate `.claude/rules/00.core/00.standard-reference.md` — a directory of all standard files. This is NOT a "read all" instruction. Claude should Read ONLY the standards relevant to the current task. Structure it as:
63
81
  ```
64
82
  ---
65
83
  paths:
66
84
  - "**/*"
67
85
  ---
68
- # Required Standard Documents
69
- Before writing or modifying code, you MUST Read the relevant standard files below using the Read tool.
70
- ## Core (always read)
86
+ # Standard Documents Directory
87
+ Below is the complete list of standard files. Read ONLY the ones relevant to your current task do NOT read all files.
88
+ Each rule file in .claude/rules/ links to its corresponding standard in the ## Reference section. Follow those links first.
89
+ This directory is for discovering standards that have no corresponding rule file.
90
+ ## Core
71
91
  - claudeos-core/standard/00.core/01.project-overview.md
72
92
  - claudeos-core/standard/00.core/02.architecture.md
73
93
  - claudeos-core/standard/00.core/03.naming-conventions.md
@@ -89,7 +89,7 @@ Analysis items (per domain):
89
89
  - Security issues (SQL Injection, missing authorization, CSRF)
90
90
  - Performance issues (unnecessary queries, memory)
91
91
 
92
- Do not create files. Analysis only.
92
+ Do not create or modify source files. Analysis only.
93
93
  Save results to claudeos-core/generated/pass1-{{PASS_NUM}}.json in the following format:
94
94
 
95
95
  {
@@ -16,6 +16,13 @@ If a standard defines a specific pattern (e.g., import path, file naming, API us
16
16
  the corresponding rule MUST use the same pattern. Before generating each rule file,
17
17
  verify it is consistent with the related standard file.
18
18
 
19
+ CRITICAL — Code Example Accuracy:
20
+ ALL code examples in rules and standards MUST use EXACT method names, class names,
21
+ and signatures from pass2-merged.json analysis data.
22
+ Do NOT paraphrase, rename, or infer API names.
23
+ If a method signature is not captured in the analysis data,
24
+ write "See corresponding standard for exact API" instead of guessing.
25
+
19
26
  CRITICAL — CLAUDE.md Reference Table Completeness:
20
27
  The reference table in CLAUDE.md MUST list ALL generated standard files, not just core.
21
28
  Include all backend-api, security-db, infra, and verification standards.
@@ -56,19 +63,31 @@ Generation targets:
56
63
  - Key rules summary table
57
64
 
58
65
  3. .claude/rules/ (active domains only)
59
- - Every rule file MUST include `paths: ["**/*"]` in YAML frontmatter — this ensures Claude Code always loads the rule regardless of which file is being edited
60
66
  - Write the full rule content directly in each file (self-contained, no external references)
61
67
  - Include 5-15 lines of key rules with concrete examples
62
68
  - Do NOT use @import — it is not a Claude Code feature and does not work
63
- - MUST generate `.claude/rules/00.core/00.standard-reference.md` a mandatory rule file that instructs Claude Code to Read the standard documents before coding. Structure it as:
69
+ - Each rule file MUST end with a `## Reference` section linking to the corresponding standard file(s):
70
+ ```
71
+ ## Reference
72
+ > For detailed patterns and examples, Read: claudeos-core/standard/10.backend-api/01.view-patterns.md
73
+ ```
74
+ - `paths:` frontmatter per rule category:
75
+ - `00.core/*` rules: `paths: ["**/*"]` — always loaded (architecture, naming are universally needed)
76
+ - `10.backend/*` rules: `paths: ["**/*"]` — always loaded (backend rules needed for any source editing)
77
+ - `30.security-db/*` rules: `paths: ["**/*"]` — always loaded (cross-cutting concerns)
78
+ - `40.infra/*` rules: `paths: ["**/*"]` — always loaded (Django settings are .py files, so infra paths cannot be scoped separately)
79
+ - `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]` — loaded only when editing claudeos-core files
80
+ - MUST generate `.claude/rules/00.core/00.standard-reference.md` — a directory of all standard files. This is NOT a "read all" instruction. Claude should Read ONLY the standards relevant to the current task. Structure it as:
64
81
  ```
65
82
  ---
66
83
  paths:
67
84
  - "**/*"
68
85
  ---
69
- # Required Standard Documents
70
- Before writing or modifying code, you MUST Read the relevant standard files below using the Read tool.
71
- ## Core (always read)
86
+ # Standard Documents Directory
87
+ Below is the complete list of standard files. Read ONLY the ones relevant to your current task do NOT read all files.
88
+ Each rule file in .claude/rules/ links to its corresponding standard in the ## Reference section. Follow those links first.
89
+ This directory is for discovering standards that have no corresponding rule file.
90
+ ## Core
72
91
  - claudeos-core/standard/00.core/01.project-overview.md
73
92
  - claudeos-core/standard/00.core/02.architecture.md
74
93
  - claudeos-core/standard/00.core/03.naming-conventions.md
@@ -93,7 +93,7 @@ Analysis items (per domain):
93
93
  - Security issues (injection, missing authorization)
94
94
  - Performance issues (blocking I/O, N+1)
95
95
 
96
- Do not create files. Analysis only.
96
+ Do not create or modify source files. Analysis only.
97
97
  Save results to claudeos-core/generated/pass1-{{PASS_NUM}}.json in the following format:
98
98
 
99
99
  {
@@ -16,6 +16,13 @@ If a standard defines a specific pattern (e.g., import path, file naming, API us
16
16
  the corresponding rule MUST use the same pattern. Before generating each rule file,
17
17
  verify it is consistent with the related standard file.
18
18
 
19
+ CRITICAL — Code Example Accuracy:
20
+ ALL code examples in rules and standards MUST use EXACT method names, class names,
21
+ and signatures from pass2-merged.json analysis data.
22
+ Do NOT paraphrase, rename, or infer API names.
23
+ If a method signature is not captured in the analysis data,
24
+ write "See corresponding standard for exact API" instead of guessing.
25
+
19
26
  CRITICAL — CLAUDE.md Reference Table Completeness:
20
27
  The reference table in CLAUDE.md MUST list ALL generated standard files, not just core.
21
28
  Include all backend-api, security-db, infra, and verification standards.
@@ -56,19 +63,31 @@ Generation targets:
56
63
  - Key rules summary table
57
64
 
58
65
  3. .claude/rules/ (active domains only)
59
- - Every rule file MUST include `paths: ["**/*"]` in YAML frontmatter — this ensures Claude Code always loads the rule regardless of which file is being edited
60
66
  - Write the full rule content directly in each file (self-contained, no external references)
61
67
  - Include 5-15 lines of key rules with concrete examples
62
68
  - Do NOT use @import — it is not a Claude Code feature and does not work
63
- - MUST generate `.claude/rules/00.core/00.standard-reference.md` a mandatory rule file that instructs Claude Code to Read the standard documents before coding. Structure it as:
69
+ - Each rule file MUST end with a `## Reference` section linking to the corresponding standard file(s):
70
+ ```
71
+ ## Reference
72
+ > For detailed patterns and examples, Read: claudeos-core/standard/10.backend-api/01.router-endpoint-patterns.md
73
+ ```
74
+ - `paths:` frontmatter per rule category:
75
+ - `00.core/*` rules: `paths: ["**/*"]` — always loaded (architecture, naming are universally needed)
76
+ - `10.backend/*` rules: `paths: ["**/*"]` — always loaded (backend rules needed for any source editing)
77
+ - `30.security-db/*` rules: `paths: ["**/*"]` — always loaded (cross-cutting concerns)
78
+ - `40.infra/*` rules: `paths: ["**/*.toml", "**/*.env*", "**/config/**", "**/Dockerfile*", "**/*.yml", "**/*.yaml"]` — loaded only for config/infra files
79
+ - `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]` — loaded only when editing claudeos-core files
80
+ - MUST generate `.claude/rules/00.core/00.standard-reference.md` — a directory of all standard files. This is NOT a "read all" instruction. Claude should Read ONLY the standards relevant to the current task. Structure it as:
64
81
  ```
65
82
  ---
66
83
  paths:
67
84
  - "**/*"
68
85
  ---
69
- # Required Standard Documents
70
- Before writing or modifying code, you MUST Read the relevant standard files below using the Read tool.
71
- ## Core (always read)
86
+ # Standard Documents Directory
87
+ Below is the complete list of standard files. Read ONLY the ones relevant to your current task do NOT read all files.
88
+ Each rule file in .claude/rules/ links to its corresponding standard in the ## Reference section. Follow those links first.
89
+ This directory is for discovering standards that have no corresponding rule file.
90
+ ## Core
72
91
  - claudeos-core/standard/00.core/01.project-overview.md
73
92
  - claudeos-core/standard/00.core/02.architecture.md
74
93
  - claudeos-core/standard/00.core/03.naming-conventions.md
@@ -47,28 +47,21 @@ function determineActiveDomains(stack) {
47
47
  function selectTemplates(stack) {
48
48
  const templates = { backend: null, frontend: null };
49
49
 
50
- // Backend template
50
+ // Backend template (requires a backend framework; language-only fallback skipped for pure frontend projects)
51
51
  if (stack.language === "kotlin") templates.backend = "kotlin-spring";
52
52
  else if (stack.language === "java") templates.backend = "java-spring";
53
53
  else if (stack.framework === "express" || stack.framework === "nestjs") templates.backend = "node-express";
54
54
  else if (stack.framework === "django") templates.backend = "python-django";
55
55
  else if (stack.framework === "fastapi" || stack.framework === "flask") templates.backend = "python-fastapi";
56
- else if (stack.language === "typescript" || stack.language === "javascript") templates.backend = "node-express";
57
- else if (stack.language === "python") templates.backend = "python-fastapi";
56
+ else if ((stack.language === "typescript" || stack.language === "javascript") && stack.framework) templates.backend = "node-express";
57
+ else if (stack.language === "python" && stack.framework) templates.backend = "python-fastapi";
58
58
 
59
59
  // Frontend template
60
60
  if (stack.frontend === "nextjs" || stack.frontend === "react" || stack.frontend === "vue") {
61
61
  templates.frontend = "node-nextjs";
62
62
  }
63
63
 
64
- // Pure frontend project with no backend
65
- if (!templates.backend && templates.frontend) {
66
- templates.backend = null;
67
- }
68
-
69
64
  return templates;
70
65
  }
71
66
 
72
- // ─── Dynamic prompt generation (multi-stack) ──────────────────────
73
-
74
67
  module.exports = { splitDomainGroups, determineActiveDomains, selectTemplates };
@@ -56,11 +56,6 @@ function generatePrompts(templates, lang, templatesDir, generatedDir) {
56
56
  }
57
57
  }
58
58
 
59
- if (primaryTemplate) {
60
- const body = readTemplate(primaryTemplate, "pass1");
61
- if (body) writeFileSafe(path.join(generatedDir, "pass1-prompt.md"), header + body);
62
- }
63
-
64
59
  if (primaryTemplate) {
65
60
  const body = readTemplate(primaryTemplate, "pass2");
66
61
  if (body) {
@@ -186,9 +186,9 @@ async function detectStack(ROOT) {
186
186
  else if (deps.react) { stack.frontend = "react"; stack.detected.push("react"); stack.frontendVersion = deps.react.replace(/[^0-9.]/g, ""); }
187
187
  else if (deps.vue) { stack.frontend = "vue"; stack.detected.push("vue"); stack.frontendVersion = deps.vue.replace(/[^0-9.]/g, ""); }
188
188
 
189
- // Backend framework
189
+ // Backend framework (NestJS checked first — more specific than express, which NestJS includes as a dependency)
190
+ if (deps["@nestjs/core"] && !stack.framework) { stack.framework = "nestjs"; stack.detected.push("nestjs"); stack.frameworkVersion = deps["@nestjs/core"].replace(/[^0-9.]/g, ""); }
190
191
  if (deps.express && !stack.framework) { stack.framework = "express"; stack.detected.push("express"); }
191
- if (deps["@nestjs/core"]) { stack.framework = "nestjs"; stack.detected.push("nestjs"); stack.frameworkVersion = deps["@nestjs/core"].replace(/[^0-9.]/g, ""); }
192
192
 
193
193
  // ORM
194
194
  if ((deps["@prisma/client"] || deps.prisma) && !stack.orm) { stack.orm = "prisma"; stack.detected.push("prisma"); }
@@ -413,7 +413,7 @@ async function scanStructure(stack, ROOT) {
413
413
  for (const dir of allDirs) {
414
414
  const name = path.basename(dir);
415
415
  if (skipPages.includes(name) || name.startsWith("(") || name.startsWith("[") || name.startsWith("_") || name.startsWith(".")) continue;
416
- const files = await glob(`${dir}**/*.{tsx,jsx,ts,js}`, { cwd: ROOT });
416
+ const files = await glob(`${dir}**/*.{tsx,jsx,ts,js,vue}`, { cwd: ROOT });
417
417
  if (files.length > 0) {
418
418
  const pages = files.filter(f => /page\.|index\./.test(f)).length;
419
419
  const layouts = files.filter(f => /layout\./.test(f)).length;
@@ -434,7 +434,7 @@ async function scanStructure(stack, ROOT) {
434
434
  for (const dir of fsdDirs) {
435
435
  const name = path.basename(dir);
436
436
  if (["ui", "common", "shared", "lib", "config", "index"].includes(name)) continue;
437
- const files = await glob(`${dir}**/*.{tsx,jsx,ts,js}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*", "**/*.stories.*"] });
437
+ const files = await glob(`${dir}**/*.{tsx,jsx,ts,js,vue}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*", "**/*.stories.*"] });
438
438
  if (files.length > 0) {
439
439
  const uiFiles = files.filter(f => /\bui\b/.test(f)).length;
440
440
  const modelFiles = files.filter(f => /model|store|hook/.test(f)).length;
@@ -448,14 +448,15 @@ async function scanStructure(stack, ROOT) {
448
448
  for (const dir of compDirs) {
449
449
  const name = path.basename(dir);
450
450
  if (["ui", "common", "shared", "layout", "icons"].includes(name)) continue;
451
- const files = await glob(`${dir}**/*.{tsx,jsx}`, { cwd: ROOT });
451
+ const files = await glob(`${dir}**/*.{tsx,jsx,vue}`, { cwd: ROOT });
452
452
  if (files.length >= 2) {
453
453
  frontendDomains.push({ name: `comp-${name}`, type: "frontend", components: files.length, totalFiles: files.length });
454
454
  }
455
455
  }
456
456
 
457
- // ── Fallback: extract domains directly from page.tsx/index.tsx locations when scanners return 0 ──
457
+ // ── Fallback: extract domains when primary scanners return 0 ──
458
458
  if (frontendDomains.length === 0) {
459
+ // Fallback A: Next.js page.tsx / client.tsx based detection
459
460
  const pageFiles = await glob("**/page.{tsx,jsx}", { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**"] });
460
461
  const domainSet = {};
461
462
  const skipNames = ["app", "src", "pages", "api", "_app", "_document"];
@@ -473,7 +474,6 @@ async function scanStructure(stack, ROOT) {
473
474
  }
474
475
  }
475
476
  }
476
- // Count client.tsx as well
477
477
  const clientFiles = await glob("**/client.{tsx,jsx}", { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**"] });
478
478
  for (const f of clientFiles) {
479
479
  const parts = f.replace(/\\/g, "/").split("/");
@@ -494,9 +494,9 @@ async function scanStructure(stack, ROOT) {
494
494
  });
495
495
  }
496
496
 
497
- // Also scan widgets/features/entities directly (glob fallback)
497
+ // Fallback B: FSD widgets/features/entities
498
498
  for (const layer of ["widgets", "features", "entities"]) {
499
- const layerFiles = await glob(`**/${layer}/*/**/*.{tsx,jsx,ts,js}`, { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**", "**/*.spec.*", "**/*.test.*"] });
499
+ const layerFiles = await glob(`**/${layer}/*/**/*.{tsx,jsx,ts,js,vue}`, { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**", "**/*.spec.*", "**/*.test.*"] });
500
500
  const layerDomains = {};
501
501
  for (const f of layerFiles) {
502
502
  const parts = f.replace(/\\/g, "/").split("/");
@@ -513,6 +513,57 @@ async function scanStructure(stack, ROOT) {
513
513
  frontendDomains.push({ name: `${layer}/${name}`, type: "frontend", totalFiles: count });
514
514
  }
515
515
  }
516
+
517
+ // Fallback C: Deep components/**/components/*/ detection (React/CRA/Vite projects)
518
+ // Scans for components/ directories at any depth (e.g., src/desktop/app/components/order/)
519
+ if (frontendDomains.length === 0) {
520
+ const deepCompDirs = await glob("**/components/*/", { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**", "**/build/**", "**/dist/**", "**/.git/**", "**/vendor/**", "**/__pycache__/**", "**/coverage/**"] });
521
+ const deepDomains = {};
522
+ const skipDomainNames = ["ui", "common", "shared", "layout", "layouts", "icons", "assets", "config", "utils", "lib", "error", "footer", "header", "inputs", "template"];
523
+ for (const dir of deepCompDirs) {
524
+ const name = path.basename(dir.replace(/\/$/, ""));
525
+ if (skipDomainNames.includes(name) || name.startsWith("_") || name.startsWith(".")) continue;
526
+ const files = await glob(`${dir.replace(/\\/g, "/")}**/*.{tsx,jsx,ts,js,vue}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*", "**/*.stories.*"] });
527
+ if (files.length >= 2) {
528
+ if (!deepDomains[name]) deepDomains[name] = { components: 0, totalFiles: 0 };
529
+ deepDomains[name].components += files.length;
530
+ deepDomains[name].totalFiles += files.length;
531
+ }
532
+ }
533
+ for (const [name, data] of Object.entries(deepDomains)) {
534
+ frontendDomains.push({ name, type: "frontend", components: data.components, totalFiles: data.totalFiles });
535
+ }
536
+ }
537
+
538
+ // Fallback D: views/screens/containers/pages/routes deep detection
539
+ // Covers: Vue (views/), React Native (screens/), legacy React (containers/),
540
+ // CRA/Vite (pages/ at any depth), React Router (routes/)
541
+ if (frontendDomains.length === 0) {
542
+ const domainDirPatterns = ["views", "screens", "containers", "pages", "routes", "modules", "domains"];
543
+ const deepDirDomains = {};
544
+ const skipDirNames = ["api", "auth", "_app", "_document", "index", "ui", "common", "shared",
545
+ "layout", "layouts", "lib", "config", "utils", "assets", "hooks", "store", "types",
546
+ "constants", "helpers", "services", "middleware", "interceptors", "guards", "decorators"];
547
+
548
+ for (const pattern of domainDirPatterns) {
549
+ const dirs = await glob(`**/${pattern}/*/`, { cwd: ROOT, ignore: ["**/node_modules/**", "**/.next/**", "**/build/**", "**/dist/**", "**/.git/**", "**/vendor/**", "**/__pycache__/**", "**/coverage/**"] });
550
+ for (const dir of dirs) {
551
+ const name = path.basename(dir.replace(/\/$/, ""));
552
+ if (skipDirNames.includes(name) || name.startsWith("_") || name.startsWith(".") || name.startsWith("(") || name.startsWith("[")) continue;
553
+ const files = await glob(`${dir.replace(/\\/g, "/")}**/*.{tsx,jsx,ts,js,vue}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*", "**/*.stories.*"] });
554
+ if (files.length >= 2) {
555
+ if (!deepDirDomains[name]) deepDirDomains[name] = { components: 0, pages: 0, totalFiles: 0, sources: [] };
556
+ const tsx = files.filter(f => /\.(tsx|jsx|vue)$/.test(f)).length;
557
+ deepDirDomains[name].components += tsx;
558
+ deepDirDomains[name].totalFiles += files.length;
559
+ if (!deepDirDomains[name].sources.includes(pattern)) deepDirDomains[name].sources.push(pattern);
560
+ }
561
+ }
562
+ }
563
+ for (const [name, data] of Object.entries(deepDirDomains)) {
564
+ frontendDomains.push({ name, type: "frontend", components: data.components, totalFiles: data.totalFiles, sources: data.sources });
565
+ }
566
+ }
516
567
  }
517
568
  }
518
569
 
@@ -173,8 +173,10 @@ async function main() {
173
173
  total++;
174
174
  const abs = path.join(ROOT, b.path);
175
175
 
176
- // Block path traversal attempts
177
- if (!path.resolve(abs).startsWith(path.resolve(ROOT) + path.sep)) {
176
+ // Block path traversal attempts (allow files at ROOT level and below)
177
+ const resolvedAbs = path.resolve(abs);
178
+ const resolvedRoot = path.resolve(ROOT);
179
+ if (resolvedAbs !== resolvedRoot && !resolvedAbs.startsWith(resolvedRoot + path.sep)) {
178
180
  console.log(` ⚠️ SKIPPED: ${b.path} (path traversal blocked)`);
179
181
  continue;
180
182
  }
@@ -81,8 +81,10 @@ async function main() {
81
81
  console.log(" [2/2] Plan → Disk...");
82
82
  for (const m of sm.mappings) {
83
83
  const abs = path.join(ROOT, m.sourcePath);
84
- // Skip path traversal attempts
85
- if (!path.resolve(abs).startsWith(path.resolve(ROOT) + path.sep)) continue;
84
+ // Skip path traversal attempts (allow files at ROOT level and below)
85
+ const resolvedAbs = path.resolve(abs);
86
+ const resolvedRoot = path.resolve(ROOT);
87
+ if (resolvedAbs !== resolvedRoot && !resolvedAbs.startsWith(resolvedRoot + path.sep)) continue;
86
88
  if (!fs.existsSync(abs)) {
87
89
  issues.orphan.push({ path: m.sourcePath, plan: m.planFile });
88
90
  }