claudeos-core 1.7.0 → 1.7.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 +17 -0
- package/bin/commands/init.js +1 -1
- package/package.json +1 -1
- package/pass-prompts/templates/python-flask/pass1.md +119 -0
- package/pass-prompts/templates/python-flask/pass2.md +85 -0
- package/pass-prompts/templates/python-flask/pass3.md +103 -0
- package/plan-installer/domain-grouper.js +2 -1
- package/plan-installer/scanners/scan-frontend.js +3 -1
- package/plan-installer/scanners/scan-java.js +226 -223
- package/plan-installer/scanners/scan-python.js +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.1] — 2026-04-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Java scanner unit tests** — New `tests/scan-java.test.js` with 18 tests covering all 5 patterns (A/B/C/D/E), supplementary scan, skip list, root package extraction, MyBatis XML detection, DDD infrastructure/ detection, and full fallback
|
|
8
|
+
- **Flask dedicated template** — New `pass-prompts/templates/python-flask/` with pass1/pass2/pass3 prompts tailored for Flask (Blueprint, @app.route, application factory, g/current_app, before_request, WTForms, Flask-SQLAlchemy, Flask-Login, Jinja2); Flask no longer shares python-fastapi template
|
|
9
|
+
- **FastAPI/Flask flat project fallback** — `scan-python.js` now detects flat projects with `main.py` or `app.py` at root (or `app/main.py`) when no router files or subdomain structure exists; covers FastAPI official tutorial structure
|
|
10
|
+
- **Vite SPA primary path scanning** — `scan-frontend.js` now detects `src/views/*/`, `src/screens/*/`, `src/routes/*/` in primary scan; Vite SPA projects no longer fall through to Fallback D
|
|
11
|
+
- **296 tests** (287 → 296) — Added 9 new tests: Flask template selection, flat project fallback (5 cases), Vite SPA primary paths (3 cases)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Java scanner Windows path normalization** — `scan-java.js` added `norm()` function and `.map(norm)` to 9 glob calls; regex matching failed on Windows backslash paths for Pattern E (DDD/Hexagonal), root package extraction, and supplementary scan
|
|
16
|
+
- **Pattern E missing infrastructure/ detection** — `scan-java.js` Pattern E `mprGlob` now includes `{domain}/infrastructure/*.java` in addition to `adapter/out/{persistence,repository}/`
|
|
17
|
+
- **Flask misusing FastAPI template** — `selectTemplates()` now routes `framework: "flask"` to dedicated `python-flask` instead of `python-fastapi`
|
|
18
|
+
- **Completion banner alignment** — `Total time:` label spacing fixed to align with other rows
|
|
19
|
+
|
|
3
20
|
## [1.7.0] — 2026-04-11
|
|
4
21
|
|
|
5
22
|
### Added
|
package/bin/commands/init.js
CHANGED
|
@@ -372,7 +372,7 @@ async function cmdInit(parsedArgs) {
|
|
|
372
372
|
log(`║ Domains analyzed: ${pad(totalGroups + " groups", 29)}║`);
|
|
373
373
|
log(`║ Analysis passes: ${pad(pass1Files + " pass1 files", 29)}║`);
|
|
374
374
|
log(`║ Output language: ${pad(SUPPORTED_LANGS[lang] || lang, 29)}║`);
|
|
375
|
-
log(`║ Total time:
|
|
375
|
+
log(`║ Total time: ${pad(formatElapsed(Date.now() - totalStart), 29)}║`);
|
|
376
376
|
log("║ ║");
|
|
377
377
|
log("║ Verify anytime: ║");
|
|
378
378
|
log("║ npx claudeos-core health ║");
|
package/package.json
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Read claudeos-core/generated/project-analysis.json and
|
|
2
|
+
perform a deep analysis of the following domains only: {{DOMAIN_GROUP}}
|
|
3
|
+
|
|
4
|
+
For each domain, select one representative file per layer, read its code, and analyze it.
|
|
5
|
+
Prioritize files with the richest patterns.
|
|
6
|
+
|
|
7
|
+
Analysis items (per domain):
|
|
8
|
+
|
|
9
|
+
1. Route/Blueprint Patterns
|
|
10
|
+
- Blueprint structure (Blueprint registration, url_prefix, naming)
|
|
11
|
+
- Route decorators (@app.route, @bp.route with methods)
|
|
12
|
+
- URL patterns and naming conventions
|
|
13
|
+
- View function structure (function-based vs class-based MethodView)
|
|
14
|
+
- Request handling (request.args, request.form, request.json, request.files)
|
|
15
|
+
- Response patterns (jsonify, make_response, Response, redirect, abort)
|
|
16
|
+
- If a custom response wrapper exists, record its EXACT function/class name, EXACT import path
|
|
17
|
+
- **Response wrapping layer (CRITICAL)**: Which layer formats the response?
|
|
18
|
+
Does the route handler call it directly, or does a service layer return formatted data?
|
|
19
|
+
- Error handling (errorhandler, abort, custom exception classes)
|
|
20
|
+
- Authentication (flask-login, flask-jwt-extended, custom decorators)
|
|
21
|
+
- API documentation (flask-restx, flask-smorest, flasgger)
|
|
22
|
+
- Pagination patterns
|
|
23
|
+
|
|
24
|
+
2. Data Model Patterns
|
|
25
|
+
- ORM (SQLAlchemy, Flask-SQLAlchemy, Peewee)
|
|
26
|
+
- Model structure (db.Model, relationships, mixins)
|
|
27
|
+
- Serialization (marshmallow, flask-marshmallow, manual to_dict)
|
|
28
|
+
- Form validation (WTForms, Flask-WTF, manual)
|
|
29
|
+
- Request/Response schema separation
|
|
30
|
+
- Enum/constant management
|
|
31
|
+
|
|
32
|
+
3. Data Access Patterns
|
|
33
|
+
- Session management (db.session, scoped_session)
|
|
34
|
+
- Repository/DAO pattern vs direct model queries
|
|
35
|
+
- Migration (Flask-Migrate / Alembic)
|
|
36
|
+
- Query optimization (eager loading, lazy loading)
|
|
37
|
+
- Connection management (pool, teardown_appcontext)
|
|
38
|
+
- Transaction management
|
|
39
|
+
|
|
40
|
+
4. Application Structure Patterns
|
|
41
|
+
- Application factory (create_app)
|
|
42
|
+
- Configuration (app.config, from_object, from_envvar)
|
|
43
|
+
- Extension initialization (db.init_app, login_manager.init_app)
|
|
44
|
+
- Context (application context, request context, g, current_app)
|
|
45
|
+
- Before/after request hooks (before_request, after_request, teardown_request)
|
|
46
|
+
- Import paths: record EXACT import conventions
|
|
47
|
+
- Utility function locations: record EXACT module paths
|
|
48
|
+
|
|
49
|
+
5. Configuration/Environment Patterns
|
|
50
|
+
- Environment variable management (python-dotenv, os.environ)
|
|
51
|
+
- Config classes (DevelopmentConfig, ProductionConfig)
|
|
52
|
+
- Per-environment branching
|
|
53
|
+
- Secret management (SECRET_KEY, database URL)
|
|
54
|
+
|
|
55
|
+
6. Logging Patterns
|
|
56
|
+
- Logger usage (app.logger, structlog, loguru, logging)
|
|
57
|
+
- Log level policy
|
|
58
|
+
- Request/response logging
|
|
59
|
+
- Error logging
|
|
60
|
+
|
|
61
|
+
7. Testing Patterns
|
|
62
|
+
- Test framework (pytest, unittest)
|
|
63
|
+
- Test client (app.test_client, pytest fixtures)
|
|
64
|
+
- Application factory testing (create_app with test config)
|
|
65
|
+
- Fixture management (conftest, client fixture)
|
|
66
|
+
- DB test strategy (test DB, SQLite in-memory, transaction rollback)
|
|
67
|
+
- Mocking (unittest.mock, pytest-mock, responses)
|
|
68
|
+
|
|
69
|
+
8. Domain-Specific Patterns
|
|
70
|
+
- Template rendering (Jinja2, render_template) vs JSON API
|
|
71
|
+
- File upload (request.files, werkzeug FileStorage)
|
|
72
|
+
- Background tasks (Celery, RQ, APScheduler)
|
|
73
|
+
- WebSocket (Flask-SocketIO)
|
|
74
|
+
- External API integration (requests, httpx)
|
|
75
|
+
- Caching (Flask-Caching, Redis)
|
|
76
|
+
- Session management (server-side, Flask-Session)
|
|
77
|
+
- CSRF protection (Flask-WTF CSRFProtect)
|
|
78
|
+
|
|
79
|
+
9. Anti-patterns / Inconsistencies
|
|
80
|
+
- Circular imports
|
|
81
|
+
- Missing application factory
|
|
82
|
+
- Global state misuse
|
|
83
|
+
- Missing error handlers
|
|
84
|
+
- Security issues (injection, missing CSRF, debug mode in production)
|
|
85
|
+
- Performance issues (blocking I/O, N+1 queries)
|
|
86
|
+
|
|
87
|
+
Do not create or modify source files. Analysis only.
|
|
88
|
+
Save results to claudeos-core/generated/pass1-{{PASS_NUM}}.json in the following format:
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
"analyzedAt": "ISO timestamp",
|
|
92
|
+
"passNum": {{PASS_NUM}},
|
|
93
|
+
"domains": ["auth", "users", "orders", "products"],
|
|
94
|
+
"analysisPerDomain": {
|
|
95
|
+
"users": {
|
|
96
|
+
"representativeFiles": {
|
|
97
|
+
"routes": "app/users/routes.py",
|
|
98
|
+
"models": "app/users/models.py",
|
|
99
|
+
"forms": "app/users/forms.py",
|
|
100
|
+
"services": "app/users/services.py"
|
|
101
|
+
},
|
|
102
|
+
"patterns": {
|
|
103
|
+
"routes": { ... },
|
|
104
|
+
"models": { ... },
|
|
105
|
+
"dataAccess": { ... },
|
|
106
|
+
"appStructure": { ... },
|
|
107
|
+
"config": { ... },
|
|
108
|
+
"logging": { ... },
|
|
109
|
+
"testing": { ... }
|
|
110
|
+
},
|
|
111
|
+
"specialPatterns": [],
|
|
112
|
+
"antiPatterns": []
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"crossDomainCommon": {
|
|
116
|
+
"description": "Patterns commonly used across domains in this group",
|
|
117
|
+
"patterns": []
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Read all pass1-*.json files from the claudeos-core/generated/ directory and
|
|
2
|
+
merge all domain analysis results into a single unified report.
|
|
3
|
+
|
|
4
|
+
Merge items:
|
|
5
|
+
|
|
6
|
+
1. Universal Patterns (shared by 100% of all domains)
|
|
7
|
+
- Route/Blueprint style (decorators, response format, status codes)
|
|
8
|
+
- **Response flow**: Which layer wraps the response? (route handler vs service layer)
|
|
9
|
+
Record the definitive answer. If an orchestration layer exists, describe it.
|
|
10
|
+
- Model/serialization conventions (SQLAlchemy, marshmallow)
|
|
11
|
+
- Data access patterns (session management, queries)
|
|
12
|
+
- Application factory structure (create_app, extensions)
|
|
13
|
+
- Error handling patterns
|
|
14
|
+
- Before/after request hooks
|
|
15
|
+
|
|
16
|
+
2. Majority Patterns (shared by 50%+ of domains)
|
|
17
|
+
- Specify which domains share them
|
|
18
|
+
|
|
19
|
+
3. Domain-Specific Patterns (unique to a single domain)
|
|
20
|
+
- File upload: which domain
|
|
21
|
+
- WebSocket: which domain
|
|
22
|
+
- Background tasks: which domain
|
|
23
|
+
- External API: which domain
|
|
24
|
+
- Caching: which domain
|
|
25
|
+
- Template rendering: which domain
|
|
26
|
+
|
|
27
|
+
4. Anti-pattern Summary
|
|
28
|
+
- Consolidate all inconsistencies found across domains
|
|
29
|
+
- Classify by severity (CRITICAL / HIGH / MEDIUM / LOW)
|
|
30
|
+
|
|
31
|
+
5. Naming Conventions Summary
|
|
32
|
+
- Module/package naming (snake_case)
|
|
33
|
+
- Blueprint naming conventions
|
|
34
|
+
- Model/schema naming conventions
|
|
35
|
+
- Route URL patterns
|
|
36
|
+
- File structure conventions
|
|
37
|
+
|
|
38
|
+
6. Common Models/Utilities List
|
|
39
|
+
- Base model mixins with EXACT import paths
|
|
40
|
+
- Shared utility functions with EXACT module paths
|
|
41
|
+
- Constants/Enum management with EXACT locations
|
|
42
|
+
- Extension instances (db, login_manager, etc.)
|
|
43
|
+
|
|
44
|
+
7. Security/Authentication Patterns
|
|
45
|
+
- Authentication method (flask-login, flask-jwt-extended, custom)
|
|
46
|
+
- Authorization decorators (login_required, custom)
|
|
47
|
+
- CSRF protection
|
|
48
|
+
- CORS configuration
|
|
49
|
+
- Environment variable management
|
|
50
|
+
|
|
51
|
+
8. Database Patterns
|
|
52
|
+
- Table naming (__tablename__)
|
|
53
|
+
- Migration strategy (Flask-Migrate)
|
|
54
|
+
- Seed data management
|
|
55
|
+
- Audit fields (created_at, updated_at)
|
|
56
|
+
- Relationship patterns
|
|
57
|
+
- Index/constraints
|
|
58
|
+
|
|
59
|
+
9. Testing Strategy Summary
|
|
60
|
+
- Test coverage level
|
|
61
|
+
- Test classification system (unit/integration/E2E)
|
|
62
|
+
- Test client setup (app.test_client)
|
|
63
|
+
- Fixture/Factory strategy
|
|
64
|
+
- Mocking strategy
|
|
65
|
+
- DB test strategy
|
|
66
|
+
|
|
67
|
+
10. Logging/Monitoring Strategy
|
|
68
|
+
- Logger standard (app.logger vs custom)
|
|
69
|
+
- Log level policy
|
|
70
|
+
- Request/response logging
|
|
71
|
+
|
|
72
|
+
11. Performance Patterns
|
|
73
|
+
- Caching strategy (Flask-Caching)
|
|
74
|
+
- Connection pool configuration
|
|
75
|
+
- Query optimization
|
|
76
|
+
- Static file serving
|
|
77
|
+
|
|
78
|
+
12. Code Quality Tools
|
|
79
|
+
- Lint/Format tools (ruff, black, isort, mypy, flake8)
|
|
80
|
+
- Pre-commit hooks
|
|
81
|
+
- Type Checking (mypy, pyright)
|
|
82
|
+
- CI integration status
|
|
83
|
+
|
|
84
|
+
Do not generate code. Merge only.
|
|
85
|
+
Save results to claudeos-core/generated/pass2-merged.json.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Read claudeos-core/generated/project-analysis.json and
|
|
2
|
+
claudeos-core/generated/pass2-merged.json, then
|
|
3
|
+
generate all ClaudeOS-Core files based on the analysis results.
|
|
4
|
+
|
|
5
|
+
Do not read the original source code again. Reference only the analysis results.
|
|
6
|
+
|
|
7
|
+
CRITICAL — Package Manager Consistency:
|
|
8
|
+
Check `stack.packageManager` in project-analysis.json (e.g., "poetry", "pipenv", "pip").
|
|
9
|
+
ALL generated files MUST use ONLY that detected package manager's commands.
|
|
10
|
+
NEVER mix pip/poetry/pipenv commands. Also check actual script names in pyproject.toml or Makefile.
|
|
11
|
+
|
|
12
|
+
CRITICAL — Cross-file Consistency:
|
|
13
|
+
Rules (.claude/rules/) and Standards (claudeos-core/standard/) MUST NOT contradict each other.
|
|
14
|
+
|
|
15
|
+
CRITICAL — Code Example Accuracy:
|
|
16
|
+
ALL code examples in rules and standards MUST use EXACT method names, class names,
|
|
17
|
+
and signatures from pass2-merged.json analysis data.
|
|
18
|
+
Do NOT paraphrase, rename, or infer API names.
|
|
19
|
+
|
|
20
|
+
CRITICAL — Response Flow Consistency:
|
|
21
|
+
Determine from pass2-merged.json which layer (route handler vs service layer) formats
|
|
22
|
+
the response. This MUST be identical across architecture.md, route-patterns.md,
|
|
23
|
+
and all rules files.
|
|
24
|
+
|
|
25
|
+
CRITICAL — CLAUDE.md Reference Table Completeness:
|
|
26
|
+
The reference table in CLAUDE.md MUST list ALL generated standard files.
|
|
27
|
+
|
|
28
|
+
Generation targets:
|
|
29
|
+
|
|
30
|
+
1. CLAUDE.md (project root)
|
|
31
|
+
- Role definition (based on detected stack — Flask)
|
|
32
|
+
- Build & Run Commands (pip/poetry, flask run, gunicorn, docker)
|
|
33
|
+
- Core architecture diagram (application factory, Blueprint structure)
|
|
34
|
+
- Module structure
|
|
35
|
+
- Standard/Skills/Guide reference table
|
|
36
|
+
|
|
37
|
+
2. claudeos-core/standard/ (active domains only)
|
|
38
|
+
- 00.core/01.project-overview.md — Stack, modules, server info
|
|
39
|
+
- 00.core/02.architecture.md — Application factory, Blueprint hierarchy, request flow
|
|
40
|
+
- 00.core/03.naming-conventions.md — Module/model/blueprint/route naming conventions
|
|
41
|
+
- 10.backend-api/01.route-blueprint-patterns.md — Blueprint structure, route decorators, request/response handling
|
|
42
|
+
- 10.backend-api/02.model-schema-patterns.md — SQLAlchemy models, marshmallow/WTForms serialization
|
|
43
|
+
- 10.backend-api/03.service-patterns.md — Service layer, business logic separation
|
|
44
|
+
- 10.backend-api/04.response-error-patterns.md — Response formatting, error handlers, custom exceptions
|
|
45
|
+
- 30.security-db/01.security-auth.md — Authentication, CSRF, session management, environment variables
|
|
46
|
+
- 30.security-db/02.database-patterns.md — SQLAlchemy patterns, migrations, relationships
|
|
47
|
+
- 40.infra/01.environment-config.md — Config classes, environment variables, extension initialization
|
|
48
|
+
- 40.infra/02.logging-monitoring.md — app.logger, request logging, error tracking
|
|
49
|
+
- 40.infra/03.cicd-deployment.md — CI/CD, gunicorn, Docker deployment
|
|
50
|
+
- 50.verification/01.development-verification.md — Build, startup, flask run
|
|
51
|
+
- 50.verification/02.testing-strategy.md — pytest, test_client, fixtures, DB testing
|
|
52
|
+
|
|
53
|
+
Each file MUST include:
|
|
54
|
+
- Correct examples (code blocks)
|
|
55
|
+
- Incorrect examples (code blocks)
|
|
56
|
+
- Key rules summary table
|
|
57
|
+
|
|
58
|
+
3. .claude/rules/ (active domains only)
|
|
59
|
+
- Write the full rule content directly in each file (self-contained)
|
|
60
|
+
- Include 5-15 lines of key rules with concrete examples
|
|
61
|
+
- Do NOT use @import
|
|
62
|
+
- Each rule file MUST end with a `## Reference` section linking to the corresponding standard
|
|
63
|
+
- `paths:` frontmatter per rule category:
|
|
64
|
+
- `00.core/*` rules: `paths: ["**/*"]`
|
|
65
|
+
- `10.backend/*` rules: `paths: ["**/*"]`
|
|
66
|
+
- `30.security-db/*` rules: `paths: ["**/*"]`
|
|
67
|
+
- `40.infra/*` rules: `paths: ["**/*.json", "**/*.env*", "**/*.cfg", "**/Dockerfile*", "**/*.yml", "**/*.yaml"]`
|
|
68
|
+
- `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]`
|
|
69
|
+
- MUST generate `.claude/rules/00.core/00.standard-reference.md` — directory of all standard files
|
|
70
|
+
|
|
71
|
+
4. .claude/rules/50.sync/ (3 sync rules)
|
|
72
|
+
- 01.standard-sync.md
|
|
73
|
+
- 02.rules-sync.md
|
|
74
|
+
- 03.skills-sync.md
|
|
75
|
+
|
|
76
|
+
5. claudeos-core/skills/ (active domains only)
|
|
77
|
+
- 10.backend-crud/01.scaffold-crud-feature.md (orchestrator)
|
|
78
|
+
- 10.backend-crud/scaffold-crud-feature/01~08 (sub-skills: blueprint, routes, model, schema, service, migration, test, index)
|
|
79
|
+
- 00.shared/MANIFEST.md (skill registry)
|
|
80
|
+
|
|
81
|
+
6. claudeos-core/guide/ (all)
|
|
82
|
+
- 01.onboarding/01.overview.md
|
|
83
|
+
- 01.onboarding/02.quickstart.md
|
|
84
|
+
- 01.onboarding/03.glossary.md
|
|
85
|
+
- 02.usage/01.faq.md
|
|
86
|
+
- 02.usage/02.real-world-examples.md
|
|
87
|
+
- 02.usage/03.do-and-dont.md
|
|
88
|
+
- 03.troubleshooting/01.troubleshooting.md
|
|
89
|
+
- 04.architecture/01.file-map.md
|
|
90
|
+
- 04.architecture/02.pros-and-cons.md
|
|
91
|
+
|
|
92
|
+
7. claudeos-core/plan/ (Master Plan)
|
|
93
|
+
- 10.standard-master.md — CLAUDE.md + all standard/ files as <file> blocks
|
|
94
|
+
- 20.rules-master.md — All rules/ (except sync) as <file> blocks
|
|
95
|
+
- 21.sync-rules-master.md — All sync rules (code block format)
|
|
96
|
+
- 30.backend-skills-master.md — All backend skills as <file> blocks
|
|
97
|
+
- 40.guides-master.md — All guide/ files as <file> blocks
|
|
98
|
+
|
|
99
|
+
8. claudeos-core/database/
|
|
100
|
+
- 01.schema-overview.md — DB schema, model relationships, migration guide
|
|
101
|
+
|
|
102
|
+
9. claudeos-core/mcp-guide/
|
|
103
|
+
- 01.mcp-overview.md — MCP server integration
|
|
@@ -54,7 +54,8 @@ function selectTemplates(stack) {
|
|
|
54
54
|
else if (stack.framework === "express") templates.backend = "node-express";
|
|
55
55
|
else if (stack.framework === "fastify") templates.backend = "node-fastify";
|
|
56
56
|
else if (stack.framework === "django") templates.backend = "python-django";
|
|
57
|
-
else if (stack.framework === "fastapi"
|
|
57
|
+
else if (stack.framework === "fastapi") templates.backend = "python-fastapi";
|
|
58
|
+
else if (stack.framework === "flask") templates.backend = "python-flask";
|
|
58
59
|
else if ((stack.language === "typescript" || stack.language === "javascript") && stack.framework && stack.framework !== "vite") templates.backend = "node-express";
|
|
59
60
|
else if (stack.language === "python" && stack.framework) templates.backend = "python-fastapi";
|
|
60
61
|
|
|
@@ -54,7 +54,7 @@ async function scanFrontendDomains(stack, ROOT) {
|
|
|
54
54
|
|
|
55
55
|
// ── Next.js/React/Vue ──
|
|
56
56
|
if (stack.frontend === "nextjs" || stack.frontend === "react" || stack.frontend === "vue") {
|
|
57
|
-
// App Router / Pages Router domains (standard + monorepo
|
|
57
|
+
// App Router / Pages Router / SPA domains (standard + monorepo + Vite SPA paths)
|
|
58
58
|
const allDirs = [
|
|
59
59
|
...await glob("{app,src/app}/*/", { cwd: ROOT }),
|
|
60
60
|
...await glob("{pages,src/pages}/*/", { cwd: ROOT }),
|
|
@@ -64,6 +64,8 @@ async function scanFrontendDomains(stack, ROOT) {
|
|
|
64
64
|
...await glob("{apps,packages}/*/src/pages/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] }),
|
|
65
65
|
// Non-standard nested page paths (e.g., src/admin/pages/*, src/dashboard/app/*)
|
|
66
66
|
...await glob("src/*/{app,pages}/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] }),
|
|
67
|
+
// Vite SPA / CRA common paths (src/views/*, src/screens/*, src/routes/*)
|
|
68
|
+
...await glob("src/{views,screens,routes}/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] }),
|
|
67
69
|
];
|
|
68
70
|
const skipPages = ["api", "_app", "_document", "fonts", "not-found", "error", "loading"];
|
|
69
71
|
for (const dir of allDirs) {
|
|
@@ -1,223 +1,226 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClaudeOS-Core — Java Structure Scanner
|
|
3
|
-
*
|
|
4
|
-
* Scans Java project directory structure to discover backend domains.
|
|
5
|
-
* Supports 5 patterns:
|
|
6
|
-
* A: controller/{domain}/*.java (layer-first)
|
|
7
|
-
* B: {domain}/controller/*.java (domain-first)
|
|
8
|
-
* C: controller/DomainController.java (flat, extract from class name)
|
|
9
|
-
* D: {module}/{domain}/controller/ (module/domain — auto-upgrade from B on conflict)
|
|
10
|
-
* E: {domain}/adapter/in/web/*.java (DDD/Hexagonal)
|
|
11
|
-
* Also includes supplementary service-only scan (all patterns) and full fallback.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const path = require("path");
|
|
15
|
-
const { glob } = require("glob");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
domainMap[d].
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeOS-Core — Java Structure Scanner
|
|
3
|
+
*
|
|
4
|
+
* Scans Java project directory structure to discover backend domains.
|
|
5
|
+
* Supports 5 patterns:
|
|
6
|
+
* A: controller/{domain}/*.java (layer-first)
|
|
7
|
+
* B: {domain}/controller/*.java (domain-first)
|
|
8
|
+
* C: controller/DomainController.java (flat, extract from class name)
|
|
9
|
+
* D: {module}/{domain}/controller/ (module/domain — auto-upgrade from B on conflict)
|
|
10
|
+
* E: {domain}/adapter/in/web/*.java (DDD/Hexagonal)
|
|
11
|
+
* Also includes supplementary service-only scan (all patterns) and full fallback.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const { glob } = require("glob");
|
|
16
|
+
|
|
17
|
+
// Normalize backslash paths from glob on Windows to forward slashes
|
|
18
|
+
const norm = (p) => p.replace(/\\/g, "/");
|
|
19
|
+
|
|
20
|
+
async function scanJavaDomains(stack, ROOT) {
|
|
21
|
+
const backendDomains = [];
|
|
22
|
+
let rootPackage = null;
|
|
23
|
+
|
|
24
|
+
const javaFiles = (await glob("src/main/java/**/*.java", { cwd: ROOT })).map(norm);
|
|
25
|
+
for (const f of javaFiles) {
|
|
26
|
+
const m = f.match(/src\/main\/java\/(.+?)\/(controller|aggregator|facade|usecase|orchestrator|service|mapper|dao|dto|entity|repository|adapter)/);
|
|
27
|
+
if (m) { rootPackage = m[1].replace(/\//g, "."); break; }
|
|
28
|
+
}
|
|
29
|
+
const domainMap = {};
|
|
30
|
+
let detectedPattern = null;
|
|
31
|
+
|
|
32
|
+
// Pattern A: controller/{domain}/*.java (layer-first — domain under controller)
|
|
33
|
+
const controllersA = (await glob("src/main/java/**/controller/*/*.java", { cwd: ROOT })).map(norm);
|
|
34
|
+
for (const f of controllersA) {
|
|
35
|
+
const m = f.match(/controller\/([^/]+)\//);
|
|
36
|
+
if (m) {
|
|
37
|
+
const d = m[1];
|
|
38
|
+
if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
|
|
39
|
+
domainMap[d].controllers++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (Object.keys(domainMap).length > 0) detectedPattern = "A";
|
|
43
|
+
|
|
44
|
+
// Pattern B/D: {domain}/controller/*.java (domain-first — controller under domain)
|
|
45
|
+
// D extends B: {module}/{domain}/controller/ — auto-upgrade to module/domain on name conflict
|
|
46
|
+
if (!detectedPattern) {
|
|
47
|
+
const controllersB = (await glob("src/main/java/**/*/controller/*.java", { cwd: ROOT })).map(norm);
|
|
48
|
+
const domainPaths = {};
|
|
49
|
+
for (const f of controllersB) {
|
|
50
|
+
const m = f.match(/\/([^/]+)\/controller\/[^/]+\.java$/);
|
|
51
|
+
if (m) {
|
|
52
|
+
const d = m[1];
|
|
53
|
+
const parentMatch = f.match(/\/([^/]+)\/([^/]+)\/controller\//);
|
|
54
|
+
const parentModule = parentMatch ? parentMatch[1] : null;
|
|
55
|
+
if (!domainPaths[d]) domainPaths[d] = [];
|
|
56
|
+
domainPaths[d].push({ file: f, module: parentModule });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If same domain name found in multiple modules, use module/domain form (Pattern D)
|
|
61
|
+
for (const [d, entries] of Object.entries(domainPaths)) {
|
|
62
|
+
const modules = [...new Set(entries.map(e => e.module).filter(Boolean))];
|
|
63
|
+
if (modules.length > 1) {
|
|
64
|
+
// Pattern D: conflict — register as module/domain
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const fullName = entry.module ? `${entry.module}/${d}` : d;
|
|
67
|
+
if (!domainMap[fullName]) domainMap[fullName] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "D", modulePath: entry.module, domainName: d };
|
|
68
|
+
domainMap[fullName].controllers++;
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
|
|
72
|
+
domainMap[d].controllers += entries.length;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(domainMap).length > 0) {
|
|
76
|
+
// Determine pattern by majority vote (B vs D)
|
|
77
|
+
const patternCounts = {};
|
|
78
|
+
for (const v of Object.values(domainMap)) patternCounts[v.pattern] = (patternCounts[v.pattern] || 0) + 1;
|
|
79
|
+
detectedPattern = Object.entries(patternCounts).sort((a, b) => b[1] - a[1])[0][0];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Pattern E: DDD/Hexagonal — {domain}/adapter/in/web/*.java or {domain}/adapter/in/rest/*.java
|
|
84
|
+
if (!detectedPattern) {
|
|
85
|
+
const controllersE = (await glob("src/main/java/**/adapter/in/{web,rest}/*.java", { cwd: ROOT })).map(norm);
|
|
86
|
+
for (const f of controllersE) {
|
|
87
|
+
const m = f.match(/\/([^/]+)\/adapter\/in\/(web|rest)\/[^/]+\.java$/);
|
|
88
|
+
if (m) {
|
|
89
|
+
const d = m[1];
|
|
90
|
+
if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "E" };
|
|
91
|
+
domainMap[d].controllers++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (Object.keys(domainMap).length > 0) detectedPattern = "E";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Pattern C: Flat structure — controller/*.java (no domain directory, extract domain from class name)
|
|
98
|
+
if (!detectedPattern) {
|
|
99
|
+
const controllersC = (await glob("src/main/java/**/controller/*.java", { cwd: ROOT })).map(norm);
|
|
100
|
+
for (const f of controllersC) {
|
|
101
|
+
const m = f.match(/\/([A-Z][a-zA-Z]*)Controller\.java$/);
|
|
102
|
+
if (m) {
|
|
103
|
+
const d = m[1].toLowerCase();
|
|
104
|
+
if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "C" };
|
|
105
|
+
domainMap[d].controllers++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (Object.keys(domainMap).length > 0) detectedPattern = "C";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Supplementary scan: detect domains without controllers (service/dao/aggregator/facade/usecase only) ──
|
|
112
|
+
// Runs for ALL detected patterns (A/B/C/D/E) to catch core-only domains
|
|
113
|
+
{
|
|
114
|
+
const serviceDirs = (await glob("src/main/java/**/*/service/*.java", { cwd: ROOT })).map(norm);
|
|
115
|
+
const mapperDirs = (await glob("src/main/java/**/*/{mapper,repository,dao}/*.java", { cwd: ROOT })).map(norm);
|
|
116
|
+
const orchestrationDirs = (await glob("src/main/java/**/*/{aggregator,facade,usecase,orchestrator}/*.java", { cwd: ROOT })).map(norm);
|
|
117
|
+
const allServiceFiles = [...serviceDirs, ...mapperDirs, ...orchestrationDirs];
|
|
118
|
+
const skipDomains = ["common", "config", "util", "utils", "base", "core", "shared", "global", "framework", "infra", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "main", "generated", "build"];
|
|
119
|
+
for (const f of allServiceFiles) {
|
|
120
|
+
const m = f.match(/\/([^/]+)\/(service|mapper|repository|dao|aggregator|facade|usecase|orchestrator)\/[^/]+\.java$/);
|
|
121
|
+
if (m) {
|
|
122
|
+
const d = m[1];
|
|
123
|
+
if (!domainMap[d] && !skipDomains.includes(d) && !/^v\d+$/.test(d)) {
|
|
124
|
+
domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: detectedPattern || "B" };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Scan service/mapper/dao/aggregator/facade/usecase/dto/xml files for each domain
|
|
131
|
+
for (const d of Object.keys(domainMap)) {
|
|
132
|
+
const p = domainMap[d].pattern;
|
|
133
|
+
const dn = domainMap[d].domainName || d;
|
|
134
|
+
let svcGlob, mprGlob, dtoGlob, aggGlob;
|
|
135
|
+
|
|
136
|
+
if (p === "A") {
|
|
137
|
+
svcGlob = `src/main/java/**/service/${d}/*.java`;
|
|
138
|
+
mprGlob = `src/main/java/**/{mapper,repository,dao}/${d}/*.java`;
|
|
139
|
+
dtoGlob = `src/main/java/**/dto/${d}/**/*.java`;
|
|
140
|
+
aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${d}/*.java`;
|
|
141
|
+
} else if (p === "B" || p === "D") {
|
|
142
|
+
svcGlob = `src/main/java/**/${dn}/service/*.java`;
|
|
143
|
+
mprGlob = `src/main/java/**/${dn}/{mapper,repository,dao}/*.java`;
|
|
144
|
+
dtoGlob = `src/main/java/**/${dn}/dto/**/*.java`;
|
|
145
|
+
aggGlob = `src/main/java/**/${dn}/{aggregator,facade,usecase,orchestrator}/*.java`;
|
|
146
|
+
} else if (p === "E") {
|
|
147
|
+
svcGlob = `src/main/java/**/${d}/{application,domain}/**/*.java`;
|
|
148
|
+
mprGlob = `src/main/java/**/${d}/{adapter/out/{persistence,repository},infrastructure}/*.java`;
|
|
149
|
+
dtoGlob = `src/main/java/**/${d}/**/{dto,command,query}/**/*.java`;
|
|
150
|
+
aggGlob = null; // DDD/Hexagonal typically doesn't use aggregator layer
|
|
151
|
+
} else {
|
|
152
|
+
// Pattern C: Flat — match domain name from file name
|
|
153
|
+
const cap = d.charAt(0).toUpperCase() + d.slice(1);
|
|
154
|
+
svcGlob = `src/main/java/**/service/${cap}*.java`;
|
|
155
|
+
mprGlob = `src/main/java/**/{mapper,repository,dao}/${cap}*.java`;
|
|
156
|
+
dtoGlob = `src/main/java/**/dto/${cap}*.java`;
|
|
157
|
+
aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${cap}*.java`;
|
|
158
|
+
}
|
|
159
|
+
// Pattern C (flat): XML may be in flat directory without domain subdirectory (e.g., mapper/OrderMapper.xml)
|
|
160
|
+
// Other patterns: XML is in domain subdirectory (e.g., mapper/order/OrderMapper.xml)
|
|
161
|
+
const capDn = dn.charAt(0).toUpperCase() + dn.slice(1);
|
|
162
|
+
const xmlGlob = p === "C"
|
|
163
|
+
? `src/main/resources/{mapper,mybatis}/**/{${dn}/${capDn}*.xml,${capDn}*.xml}`
|
|
164
|
+
: `src/main/resources/{mapper,mybatis}/**/${dn}/*.xml`;
|
|
165
|
+
|
|
166
|
+
const svc = await glob(svcGlob, { cwd: ROOT });
|
|
167
|
+
const mpr = await glob(mprGlob, { cwd: ROOT });
|
|
168
|
+
const dto = await glob(dtoGlob, { cwd: ROOT });
|
|
169
|
+
const xml = await glob(xmlGlob, { cwd: ROOT });
|
|
170
|
+
const agg = aggGlob ? await glob(aggGlob, { cwd: ROOT }) : [];
|
|
171
|
+
domainMap[d].services = svc.length + agg.length;
|
|
172
|
+
domainMap[d].mappers = mpr.length;
|
|
173
|
+
domainMap[d].dtos = dto.length;
|
|
174
|
+
domainMap[d].xmlMappers = xml.length;
|
|
175
|
+
const totalFiles = svc.length + agg.length + mpr.length + dto.length + xml.length + domainMap[d].controllers;
|
|
176
|
+
backendDomains.push({ name: d, type: "backend", ...domainMap[d], totalFiles });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Java fallback: extract domains directly from all .java files when glob returns 0 ──
|
|
180
|
+
if (backendDomains.length === 0) {
|
|
181
|
+
const allJava = (await glob("**/*.java", { cwd: ROOT, ignore: ["**/node_modules/**", "**/build/**", "**/target/**", "**/test/**", "**/generated/**"] })).map(norm);
|
|
182
|
+
const javaDomains = {};
|
|
183
|
+
const skipNames = ["common", "config", "util", "utils", "base", "shared", "global", "framework", "infra", "api", "main", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "generated", "build"];
|
|
184
|
+
const versionPattern = /^v\d+$/;
|
|
185
|
+
const layerNames = ["controller", "aggregator", "facade", "usecase", "orchestrator", "service", "mapper", "repository", "dao", "dto", "vo", "entity", "adapter"];
|
|
186
|
+
|
|
187
|
+
for (const f of allJava) {
|
|
188
|
+
const parts = f.replace(/\\/g, "/").split("/");
|
|
189
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
190
|
+
if (layerNames.includes(parts[i])) {
|
|
191
|
+
const prevDir = parts[i - 1];
|
|
192
|
+
const nextDir = parts[i + 1];
|
|
193
|
+
|
|
194
|
+
// {domain}/layer/ pattern (domain before layer)
|
|
195
|
+
if (i > 0 && !skipNames.includes(prevDir) && !layerNames.includes(prevDir) && !prevDir.includes(".") && !versionPattern.test(prevDir)) {
|
|
196
|
+
if (!javaDomains[prevDir]) javaDomains[prevDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
|
|
197
|
+
if (parts[i] === "controller") javaDomains[prevDir].controllers++;
|
|
198
|
+
else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[prevDir].services++;
|
|
199
|
+
else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[prevDir].mappers++;
|
|
200
|
+
else if (["dto", "vo"].includes(parts[i])) javaDomains[prevDir].dtos++;
|
|
201
|
+
}
|
|
202
|
+
// layer/{domain}/ pattern (layer before domain)
|
|
203
|
+
if (nextDir && !nextDir.endsWith(".java") && !skipNames.includes(nextDir) && !layerNames.includes(nextDir) && !versionPattern.test(nextDir)) {
|
|
204
|
+
if (!javaDomains[nextDir]) javaDomains[nextDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
|
|
205
|
+
if (parts[i] === "controller") javaDomains[nextDir].controllers++;
|
|
206
|
+
else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[nextDir].services++;
|
|
207
|
+
else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[nextDir].mappers++;
|
|
208
|
+
else if (["dto", "vo"].includes(parts[i])) javaDomains[nextDir].dtos++;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const [d, data] of Object.entries(javaDomains)) {
|
|
216
|
+
const total = data.controllers + data.services + data.mappers + data.dtos;
|
|
217
|
+
if (total > 0) {
|
|
218
|
+
backendDomains.push({ name: d, type: "backend", ...data, totalFiles: total });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { backendDomains, rootPackage };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = { scanJavaDomains };
|
|
@@ -56,6 +56,27 @@ async function scanPythonDomains(stack, ROOT) {
|
|
|
56
56
|
if (files.length > 0) backendDomains.push({ name, type: "backend", totalFiles: files.length });
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
// Flat project fallback: main.py or app.py at root or in app/ directory with no subdomain structure
|
|
60
|
+
if (backendDomains.filter(d => d.type === "backend").length === 0) {
|
|
61
|
+
const flatEntries = await glob("{main,app}.py", { cwd: ROOT, ignore: ["**/venv/**", "**/.venv/**"] });
|
|
62
|
+
if (flatEntries.length > 0) {
|
|
63
|
+
const allPy = await glob("*.py", { cwd: ROOT, ignore: ["**/venv/**", "**/.venv/**", "setup.py", "conftest.py"] });
|
|
64
|
+
if (allPy.length > 0) {
|
|
65
|
+
backendDomains.push({ name: "app", type: "backend", totalFiles: allPy.length, flat: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Also check app/ directory with main.py but no subdirectories
|
|
69
|
+
if (backendDomains.filter(d => d.type === "backend").length === 0) {
|
|
70
|
+
const appMain = await glob("{app,src/app}/{main,app}.py", { cwd: ROOT, ignore: ["**/venv/**", "**/.venv/**"] });
|
|
71
|
+
if (appMain.length > 0) {
|
|
72
|
+
const dir = path.dirname(appMain[0]).replace(/\\/g, "/");
|
|
73
|
+
const appPy = await glob(`${dir}/*.py`, { cwd: ROOT });
|
|
74
|
+
if (appPy.length > 0) {
|
|
75
|
+
backendDomains.push({ name: path.basename(dir), type: "backend", totalFiles: appPy.length, flat: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
59
80
|
}
|
|
60
81
|
|
|
61
82
|
return { backendDomains };
|