claudeos-core 2.4.4 → 2.5.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 +125 -0
- package/README.de.md +12 -10
- package/README.es.md +12 -10
- package/README.fr.md +12 -10
- package/README.hi.md +12 -10
- package/README.ja.md +12 -10
- package/README.ko.md +12 -10
- package/README.md +12 -10
- package/README.ru.md +12 -10
- package/README.vi.md +12 -10
- package/README.zh-CN.md +12 -10
- package/bin/commands/init.js +121 -24
- package/bin/commands/lint.js +2 -0
- package/bin/commands/memory.js +10 -3
- package/content-validator/index.js +82 -13
- package/lib/env-parser.js +98 -12
- package/lib/memory-scaffold.js +35 -16
- package/manifest-generator/index.js +15 -4
- package/package.json +92 -92
- package/pass-json-validator/index.js +1 -1
- package/pass-prompts/templates/angular/pass3.md +2 -1
- package/pass-prompts/templates/common/claude-md-scaffold.md +1 -1
- package/pass-prompts/templates/common/pass3a-facts.md +11 -9
- package/pass-prompts/templates/common/pass4.md +3 -3
- package/pass-prompts/templates/java-spring/pass1.md +10 -2
- package/pass-prompts/templates/java-spring/pass3.md +5 -4
- package/pass-prompts/templates/kotlin-spring/pass3.md +2 -2
- package/pass-prompts/templates/node-express/pass3.md +1 -1
- package/pass-prompts/templates/node-fastify/pass3.md +1 -0
- package/pass-prompts/templates/node-nestjs/pass3.md +1 -0
- package/pass-prompts/templates/node-nextjs/pass3.md +1 -1
- package/pass-prompts/templates/node-vite/pass3.md +1 -0
- package/pass-prompts/templates/python-django/pass3.md +1 -1
- package/pass-prompts/templates/python-fastapi/pass3.md +1 -1
- package/pass-prompts/templates/python-flask/pass3.md +1 -0
- package/pass-prompts/templates/vue-nuxt/pass3.md +1 -0
- package/plan-installer/domain-grouper.js +4 -1
- package/plan-installer/index.js +26 -7
- package/plan-installer/jvm-detect.js +562 -0
- package/plan-installer/pass3-context-builder.js +10 -0
- package/plan-installer/prompt-generator.js +18 -2
- package/plan-installer/scanners/scan-frontend.js +67 -6
- package/plan-installer/scanners/scan-java.js +214 -15
- package/plan-installer/scanners/scan-kotlin.js +68 -3
- package/plan-installer/scanners/scan-node.js +115 -0
- package/plan-installer/scanners/scan-python.js +56 -0
- package/plan-installer/source-paths.js +61 -0
- package/plan-installer/stack-detector.js +726 -51
- package/plan-installer/structure-scanner.js +15 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Quick navigation to recent releases:
|
|
6
6
|
|
|
7
|
+
- [`2.5.1`](#251--2026-09-08) — Legacy JVM detection (non-Boot Spring 1.x–6.x, Ant / Ivy, Eclipse / IntelliJ / NetBeans metadata, `WEB-INF/lib` jars, `web.xml`, Maven multi-module and sibling projects, eGovFrame), `.env` key-name redaction gaps closed, `.env.example` DB detection, LF line endings
|
|
8
|
+
- [`2.5.0`](#250--2026-09-07) — Correctness release: append-only `decision-log.md`, read-only `health`, user-owned rules survive `--force`, flat-layout Java detection, Java multi-module + Next.js route groups, orchestrator-written path allowlist, `.env` credential masking
|
|
7
9
|
- [`2.4.4`](#244--2026-05-04) — Translation polish for `docs/{lang}/` × 8 non-Korean languages + broken `#quick-start` anchor fix
|
|
8
10
|
- [`2.4.3`](#243--2026-04-27) — Skills catalog reconciliation (MANIFEST ↔ §6 sync) + `STALE_PATH` naming-convention placeholder exemption
|
|
9
11
|
- [`2.4.2`](#242--2026-04-26) — README structural tightening + 9-language re-sync (same-day after v2.4.1 docs overhaul)
|
|
@@ -26,6 +28,129 @@ For older entries scroll past v1.5.0 or use the GitHub blame view.
|
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
31
|
+
## [2.5.1] — 2026-09-08
|
|
32
|
+
|
|
33
|
+
Patch release. Two things: the v2.5.0 credential-masking headline had a hole in it, and `init` on pre-Boot Java projects — the shape of most enterprise/SI codebases — reported "No language detected" and stopped. One new file (`plan-installer/jvm-detect.js`, pure text functions). `project-analysis.json` gains optional, additive fields only (`stack.packaging`, `stack.springFrameworkVersion`, `stack.sourceLayout`); no existing field changes type or meaning, and the `detected` array of every Spring Boot project is byte-identical to v2.5.0 output. Test suite: 825 → **974 / 974** pass (149 added, 0 changed).
|
|
34
|
+
|
|
35
|
+
### Fixed — safety
|
|
36
|
+
|
|
37
|
+
- **`.env` key-name redaction had gaps.** v2.5.0 masked URL userinfo and connection parameters (`?password=`), and its `PARAM_RE` accepted `pass` as a parameter name — but the key-name rule beside it did not, so `DB_PASS=hunter2` was written verbatim into `project-analysis.json`. Closed, together with the other abbreviated and less-common names found by a 71-name sweep: `PASS` / `PW` (segment-anchored: `DB_PASS`, `ADMIN_PW`, `MYSQL_PASS` are redacted; `BYPASS_AUTH`, `PASSENGER_APP_ENV`, `COMPASS_URL`, `PWD` are not), `PASSPHRASE`, `PEPPER` (trailing segment only — `PEPPER_ROUNDS` is a cost parameter), `SSH_KEY` (anchored — `SSH_KEYSCAN_HOSTS` is a host list), `SIGNING_KEY`, `MASTER_KEY`, `DEPLOY_KEY`, `LICENSE_KEY`, `SERVER_KEY` (each anchored — `MASTER_KEYSPACE`, `SERVER_KEYSTORE_PATH` survive), and `SERVICE_ACCOUNT` / `SERVICE_ACCOUNT_{KEY,JSON,FILE,SECRET,TOKEN,CREDENTIALS}` (but not `SERVICE_ACCOUNT_EMAIL` / `_NAME` / `_ID`, which identify the account rather than authenticate as it). Deliberately **not** a blanket `*_KEY` rule: `ROUTING_KEY`, `PARTITION_KEY`, `SORT_KEY`, `IDEMPOTENCY_KEY`, `FOREIGN_KEY_CHECKS` are architecture facts the generated docs depend on. Known accepted over-match: a leading `PASS_` segment (`PASS_RATE`) — narrowing it to a trailing segment would drop `DB_PASS_2` / `PASS_FILE`, and the two failure modes are not symmetric. Both corpora (71 secret names, 53 benign names) are pinned as tests. End-to-end: seven canary secrets planted in `.env.example` appear in **none** of the six generated files; in v2.5.0 five of them did.
|
|
38
|
+
- **`.env.example`-only repositories never had their database detected.** `lib/env-parser.js` has always treated `.env.example` as canonical (it heads `ENV_FILE_ORDER`), but the DB-type scan in `stack-detector` read only `.env` / `.env.local` / `.env.development` — files that are gitignored in most repos — so a fresh clone with a committed `DATABASE_URL=postgres://…` template reported `database: null`. Templates (`.env.example`, `.env.sample`, `.env.template`) are now consulted, under two guards that keep the change strictly additive: only when no runtime env file *declared* a `DATABASE_URL` (a `jdbc:oracle:thin:@…` the keyword list does not cover still counts as declared), and only when no other source — build file, `application.yml`, **`schema.prisma`** — has already identified a database. The fallback runs *after* the Prisma block for that reason: a placeholder can never win the `if (!stack.database)` race against a declaration.
|
|
39
|
+
|
|
40
|
+
### Added — legacy JVM detection
|
|
41
|
+
|
|
42
|
+
`init` previously set `language: "java"` from a Gradle file only when the string `spring-boot` appeared, and never set a framework or its version from Maven unless Boot was present. A 32-case matrix of real enterprise build-file shapes passed 1 case; the release passes a 103-case matrix — 66 detection shapes plus 37 false-positive / regression guards — every case pinned as a test. Nothing is inferred from a framework default: **every version string returned is a substring of a build file, a jar name, or a property/variable defined in the same project**, and an unresolvable `${var}` yields `null`, never the literal.
|
|
43
|
+
|
|
44
|
+
- **Gradle.** `apply plugin: 'java' | 'java-library' | 'war' | 'ear' | 'application'`, the `plugins { id '…' }` DSL, Kotlin-DSL bare identifiers (`java`, `war`, `` `java-library` ``) and `apply(plugin = "…")` are all Java evidence on their own. A dependency whose group is **exactly** `org.springframework` (`spring-webmvc`, `spring-context`, the 2.x single `spring` jar, `spring-framework-bom`) — in coordinate form, `group:/name:/version:` notation, `platform()` / `mavenBom`, or a version catalog `module = "org.springframework:spring-…"` with `version.ref` — yields `framework: "spring-framework"` with the version; `org.springframework.{boot,security,data,cloud}` are other projects and are never mistaken for it. `${springVersion}` resolves against `ext { }`, `def` / `val`, and **`gradle.properties`** (in-file wins). Spring Boot 1.x/2.x `buildscript { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.22.RELEASE") }` + `apply plugin: 'spring-boot'` now yields the Boot version (the old regexes anchored on a `version` keyword that form does not have). Root `subprojects { }` blocks and sub-module build files are swept with the same rules; a `war` / `ear` plugin sets `packaging`.
|
|
45
|
+
- **Maven.** `<packaging>` is reported (only when declared — the implicit `jar` default is not written back). A `<dependency>` whose `<groupId>` is exactly `org.springframework` (comment-stripped, so a `<!-- … -->`-disabled dependency is ignored) yields `spring-framework`; the version comes from `spring-framework-bom` in `<dependencyManagement>`, the first versioned Framework dependency, or a `<spring.version>` / `<spring-framework.version>` / **`<spring.maven.version>`** property, with `${prop}` resolved in-file. Spring Boot's version is now also read from `spring-boot-starter-parent`'s `<version>` (bounded to the `<parent>` block) and from a `spring-boot-dependencies` BOM import — previously only a `<spring-boot.version>` property was recognized, so `frameworkVersion` was `null` for every Initializr-generated pom. Java level: `<maven.compiler.release>` and the Maven-2-era `maven-compiler-plugin` `<configuration><source>1.5</source>` (bounded to the compiler plugin's own `<plugin>` block). **Multi-module:** a root `<packaging>pom</packaging>` with `<modules>` has its listed child poms (≤30) swept for framework, versions, ORM, DB and Java level, filling only what the root left null; `${prop}` in a child resolves against the child first, then the root `<properties>`.
|
|
46
|
+
- **Ant / Ivy / Eclipse WTP / no build tool.** `build.xml` → `buildTool: "ant"`, Java level from `<javac source="1.6">`; `ivy.xml` `<dependency org="org.springframework" name="spring-webmvc" rev="…"/>` (org exact-match, name whitelist) → framework + version, plus ORM / DB keywords. `.classpath` / `.project` → Java level from the JRE container (`JavaSE-1.7`, `J2SE-1.5`), `javanature`. **`**/{WEB-INF/lib,lib,libs}/*.jar`** (≤500 names) → `packaging: "war"` when under `WEB-INF`, Spring Framework / Boot presence and version parsed from the jar **name** (`spring-webmvc-3.0.5.RELEASE.jar`; an unversioned Spring-2.0-era `spring.jar` reports the framework with version `null`), JDBC drivers (`ojdbc*`, `postgresql-`, `mysql-connector`, `mariadb-java-client`, `h2-`, `sqlite-jdbc`, `mssql-jdbc` / `sqljdbc`, `db2jcc`, and the Korean-market **Tibero / Altibase / Cubrid**) and ORM jars (`ibatis-*` → `ibatis`, `mybatis-*`, `hibernate-*` / `eclipselink` / `openjpa` → `jpa`, `jooq-`). The jar scan also runs for Gradle/Maven projects whose build file still resolves from `fileTree(dir: 'lib')` instead of coordinates — common in codebases that adopted a build tool without migrating the jars — and fills nulls only. `**/*.java` is the last-resort language signal.
|
|
47
|
+
- **Spring 1.x.** Spring 1.0–1.2 published under the bare group `springframework` (no `org.`); Maven `<groupId>springframework</groupId>`, Gradle `'springframework:spring:1.2.9'` and Ivy `org="springframework"` are recognized alongside `org.springframework`. Still exact-match: `myspringframework` / `springframework.fake` are not it.
|
|
48
|
+
- **Gradle declaration long tail.** `group:` / `name:` / `version:` map notation in **any key order**, single- or multi-line; `${project.springVersion}` / `${rootProject.ext.springVersion}` / `${ext.x}` prefixes; Groovy version maps (`ext { versions = [spring: '…'] }` → `${versions.spring}`); buildSrc Kotlin constants (`object Versions { const val spring = "…" }` → `${Versions.spring}`); definitions pulled in through `apply from: 'gradle/dependencies.gradle'` (≤10 scripts, appended to the resolution text only); `options.release = 17` / `.set(17)`; a root holding only `settings.gradle{,.kts}`; `struts` / `struts2` / `jsf` coordinates add a tag in `detected` without setting a framework.
|
|
49
|
+
- **Repository shapes.** No root build file but sibling projects one directory down (`erp-web/pom.xml`, `erp-batch/pom.xml`) are absorbed with the Maven `<modules>` rules (`buildTool: "maven"`, depth-1 only, ≤30). JDBC coordinates for **MSSQL** (`mssql-jdbc`, `sqljdbc`, `jtds`, `sqlserver`), **DB2** (`com.ibm.db2`, `db2jcc`) and the Korean-market **Tibero / Altibase / Cubrid** are on the shared DB keyword list (additive to `stack.databases`; the primary-DB race is unchanged).
|
|
50
|
+
- **IDE metadata as evidence.** `.settings/org.eclipse.jdt.core.prefs` `compiler.compliance` (outranks the JRE container name), custom JRE names in `.classpath` (`jdk1.6.0_45`, `jdk-17.0.2`), `.classpath` `kind="lib"` / `kind="var"` jar paths (jars that are *not committed* — `M2_REPO/…/spring-webmvc-3.0.5.RELEASE.jar` still names the version), IntelliJ `.idea/misc.xml` `languageLevel="JDK_1_7"`, NetBeans `nbproject/project.properties` (`javac.source`, `file.reference.*.jar`). Jars under `lib/spring/`, `lib/db/` sub-folders are found (recursive under the lib roots).
|
|
51
|
+
- **Deployment descriptor and Spring XML.** `WEB-INF/web.xml` naming `org.springframework.web.servlet.DispatcherServlet` / `ContextLoaderListener` is Spring MVC evidence and a `war` signal; `ActionServlet` / `StrutsPrepareAndExecuteFilter` add a `struts` / `struts2` tag; `<web-app version="2.5">` adds `servlet 2.5`. `src/test/**` is excluded (a test-resources `web.xml` is not deployment evidence), and the whole block is skipped for Spring Boot projects — Boot's WAR packaging comes from the build file, and a Boot project's `detected` array stays byte-identical to v2.5.0; Struts / JSF coordinate tags are likewise not added to Boot projects. Spring XML configs' schema locations (`spring-beans-3.0.xsd`) yield a **major.minor** version — the lowest-fidelity source, consulted only when nothing else pinned a version, never overriding one (`spring-xsd 3.0` in `detected`).
|
|
52
|
+
- **Precedence against Node / Python.** All of the above runs *after* the Node and Python blocks and may only fill a language nobody claimed — or reclaim a *provisional* one: a root `package.json` that exists for gulp / jQuery / Tailwind tooling (no Node framework, no frontend framework detected) beside `build.xml`, a `.project` with `javanature`, a sibling pom/gradle, or a `WEB-INF/web.xml`. A Next.js / Express / Django project with a stray `.idea/misc.xml` or a vendored `tools/lib/*.jar` is never flipped to Java; a jar directory with no `*.java` sources claims nothing (no language, no framework, no DB). The reclaim is *parked*, not committed: if no JVM block then actually claims the project — a `build.xml` that is Phing or an empty stub, no `*.java` anywhere — the Node language is restored unchanged, and a `<project>` root element alone is not Ant (a `<javac>` task or `*.java` sources is required). A reclaimed Java project takes the JVM package manager (`ant` / `maven`), not the asset tooling's `npm`. EUC-KR-encoded poms with Korean comments detect normally (coordinates are ASCII).
|
|
53
|
+
- **eGovFrame (전자정부 표준프레임워크).** `egovframework.rte[.*]` coordinates (Maven or Gradle, `${egovframework.rte.version}` resolved) are reported as `spring-framework` — the templates and prompts understand Spring, and eGovFrame is Spring MVC underneath — plus an `egovframe <version>` tag in `detected`. Its `<spring.maven.version>` property is on the Framework-version property list.
|
|
54
|
+
- **`scan-java` reads legacy source roots.** Every pattern in the scanner is written against `src/main/java` / `src/main/resources`; those two leading segments are now rewritten per discovered root instead of being literal. Roots, in order: every `[<module>/]src/main/java` (unchanged, and if any exists the legacy fallbacks are **not** consulted — a modern tree with a stray top-level `src/` cannot be mis-rooted); otherwise `.classpath` `<classpathentry kind="src" path="…"/>` entries (test folders excluded — including a plain `test/` that `src/test/**` ignore rules would not catch), `build.xml` `<javac srcdir="${src}">` with `<property>` resolution, then `src/java`, `src`, `JavaSource`, `java`, `WebContent/WEB-INF/src` when they hold `*.java` (nested pairs keep the deeper one). For a legacy root the resources root is the java root itself — iBatis-era projects keep sqlmap XML beside the classes. `stack.sourceLayout: "legacy"` is set when a legacy root is in use. An Ant + `WEB-INF/lib` project with `src/com/acme/erp/{controller,service,dao}/` now yields `java 6 · spring-framework 3.0.5.RELEASE · oracle · ibatis · ant · war` and its three Pattern C domains; before, `init` reported "No language detected".
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- **Line endings are LF, enforced.** A `.gitattributes` (`* text=auto eol=lf`, `*.sh eol=lf`, image types `binary`) is added. Every committed blob was already LF (`git ls-files --eol` at v2.5.0: 281 text files, all `i/lf`); the CRLF that showed up on Windows checkouts of the non-Korean `docs/{lang}/` sets, `package.json`, `.gitignore` and the CI workflow came from `core.autocrlf=true` smudging on checkout, not from the repository. The attribute file makes the working tree LF on every platform regardless of that setting and stops a CRLF blob from ever being committed. No file content changes; run `git add --renormalize .` once after pulling if `git status` lists unchanged files as modified. The suite was run on Windows with the attribute file in place (974 / 974).
|
|
59
|
+
- **Templates.** `java-spring/pass1.md` and `pass3.md` no longer assume Spring Boot: when `project-analysis.json` says `framework: "spring-framework"` the Pass 1 prompt reads `WEB-INF/web.xml` and the Spring XML configs instead of a non-existent `application.yml`, and is told not to describe Boot features (auto-configuration, starters, actuator) for a project that does not declare Boot. `pass-json-validator` treats `spring-framework` as a backend framework alongside `spring-boot`.
|
|
60
|
+
- **Docs.** `docs/stacks.md` (10 languages) lists the redaction key-name families that now apply, so the documented rule matches the code again. Supported Stacks in `README.md` names the non-Boot Java shapes, and `docs/stacks.md` gains a legacy-JVM section with a per-build-form evidence table, the version policy, the precedence chain, the false-positive guards and the source-root order. **Both are translated into all 10 languages** — identifiers, coordinates and the 11-row evidence table are byte-identical across them; only prose is localized.
|
|
61
|
+
|
|
62
|
+
### Known limits (documented, unchanged)
|
|
63
|
+
|
|
64
|
+
- A password containing a raw `/`, `?` or `#` inside a URL's userinfo is not masked (`postgres://u:p/w@host/db`). These are RFC 3986 delimiters — an `@` after the first `/` belongs to the path (`https://cdn.example.com/npm/@scope/pkg`), and rewriting it would replace the real host — so the v2.5.0 rule stands; such a value must be percent-encoded (`%2F`). It is the one combination the key-name rule does not backstop, because the key is `DATABASE_URL`.
|
|
65
|
+
- Gradle build files are not comment-stripped (Maven poms are), so a `//`-commented Spring coordinate still counts — as it always has for Boot.
|
|
66
|
+
- Cross-file inheritance (a corporate parent pom that itself extends `spring-boot-starter-parent`; a version held only in a parent pom outside the repository) is out of scope; the resulting `null` falls through to LLM-side analysis.
|
|
67
|
+
- eGovFrame's default layout names its controller layer `web/` rather than `controller/`; the scanner's Pattern A/B do not yet recognize `web/` as a layer, so an `example/{web,service}/{user,board}/` tree yields a spurious `example` Pattern B domain beside the real ones. Detection output on that layout is identical to v2.5.0; a pattern change is a separate release.
|
|
68
|
+
- The stack-detector still passes the **redacted** `envInfo.vars` to `extractPort()` (`stack-detector.js`, `const vars = envInfo.vars || {}`). No current key-name rule intersects a port/host/api-target key (asserted across nine port-key variants), but a future blanket rule would break port detection silently; passing raw vars there is a structural follow-up.
|
|
69
|
+
|
|
70
|
+
### Migration
|
|
71
|
+
|
|
72
|
+
None required. New `project-analysis.json` fields are additive and `null` for projects that do not declare them. Projects that previously aborted `init` with "No language detected" on a pre-Boot Java layout will now proceed through the Java pipeline; review the detected `framework` / `frameworkVersion` / `packaging` in the Phase 1 summary the first time.
|
|
73
|
+
|
|
74
|
+
## [2.5.0] — 2026-09-07
|
|
75
|
+
|
|
76
|
+
Correctness release driven by a full-source audit of v2.4.4. No new dependencies, no new files. `project-analysis.json` gains optional, additive fields only (`stack.frontendRoot`, `stack.frontendBundler`, `stack.frontendPort`, `stack.frontendEnvInfo`, domain `pattern: "layer-first"`); pass-marker schemas are unchanged. Minor version bump because five behaviors that users could observe changed on purpose (see **Behavior changes** and **Migration**). Test suite: 736 → **825 / 825** pass (89 added, 2 legacy assertions replaced).
|
|
77
|
+
|
|
78
|
+
### Fixed — data loss / safety
|
|
79
|
+
|
|
80
|
+
- **`memory compact` no longer touches `decision-log.md`.** Stage 1 ("summarize aged entries") dropped the Context / Options / Decision / Consequences body of every entry older than 30 days — i.e. the "why" the file exists to preserve — while the generated `60.memory/01.decision-log.md` rule and CLAUDE.md §8 promised "permanent, append-only". Pass 4 seeds 3–5 entries dated today, so every project hit this on its first compaction after a month. Compaction now applies to `failure-patterns.md` only; `decision-log.md` is logged as "append-only, never compacted". Scaffold text (`compaction.md`, `60.memory/03.compaction.md`, `pass4.md`) and `docs/commands.md` updated to match.
|
|
81
|
+
- **`npx claudeos-core health` is read-only again.** Since v2.4.3, `health` → `manifest-generator` → `skills-sync` silently patched `CLAUDE.md` §6 and `MANIFEST.md`. A gate documented for CI / pre-commit must not dirty the working tree. Skills reconciliation now runs only when `manifest-generator` is invoked with the `--sync-skills` flag; `init` passes it after Pass 3/4, `health` does not. There is deliberately no environment-variable switch — `health` spawns `manifest-generator` with the inherited environment, so an exported variable would have re-enabled writes from a shell that set it for `init`.
|
|
82
|
+
- **`--force` / "fresh" preserve user-owned files under `.claude/rules/`.** Previously the whole directory was `rmSync`'d, including rule files the user had written before ever running claudeos-core. Only the claudeos-core-managed `NN.` category *directories* (`00.core/` … `90.optional/`) are wiped now (`wipeManagedRuleCategories()`, exported and unit-tested); a `NN.`-prefixed *file* at the rules root (`.claude/rules/01.team-style.md`) is user-authored — the tool never writes one there — and survives. Guard 2 (zero-rules detection) counts only those categories so a preserved user file cannot mask a silent Pass 3 failure.
|
|
83
|
+
- **`.env` credential masking is complete.** The `DATABASE_URL` redaction exemption is gone — its stated justification ("stack-detector reads it") was stale (stack-detector scans raw `.env` text and never reads `envInfo.vars`), while `postgres://user:password@host/db` was written verbatim into `project-analysis.json`, which Pass 3/4 prompts instruct the model to read. Every URL-shaped value (`DATABASE_URL`, `REDIS_URL`, `MONGO_URI`, `jdbc:postgresql://…`, …) now has its userinfo masked to `***:***` with scheme / host / path intact; `envInfo.host` and `envInfo.apiTarget` are masked the same way. Userinfo is everything between `://` and the last `@` of the authority, so a password containing `@` (`p@ss`) is masked whole; it may not contain `/`, `?` or `#`, so an `@` in a path or query (`https://cdn.example.com/npm/@scope/pkg`, `/users/@me`, `?redirect=user@host`) is never rewritten and the real host is never replaced. Scheme-less credentials are recognized only in the Go/MySQL DSN shape (`user:pw@tcp(db:3306)/db`, `@unix(…)`) — a generic `a:b@c` rule would corrupt `mailto:ops@example.com` or `0:30@daily`. A raw `/` inside a password is not a valid URL and is deliberately left alone. Credentials carried as connection *parameters* are masked as well — `?password=` / `;password=` / `pwd` / `secret` / `token` / `access_key` / `api_key` values become `***` with the parameter name kept (`jdbc:postgresql://db/app?user=app&password=***`, `sqlserver://host;user=sa;password=***`), and a Go DSN password containing `@` (`user:p@ss@tcp(h:3306)/db`) is masked whole.
|
|
84
|
+
- **CI time bomb.** `tests/memory-command.test.js` carried five hard-coded `2026-04-xx` dates; from 2026-05-17 one assertion aged past the 30-day compaction window and CI went red. All five use a relative `daysAgo(n)` helper now.
|
|
85
|
+
- **Stale translations in the static-fallback cache.** `claudeos-core/generated/fallback-cache-<lang>.json` was keyed by content *name* (`MEMORY_FILES.compaction.md`), so once a project had a cache, every later change to a static-fallback text (this release changes three: `compaction.md`, `60.memory/03.compaction.md`, `52.ai-work-rules.md`) kept serving the translation of the old English text. Keys now include a hash of the English source (`<name>@<sha1[0:12]>`); a text change is a cache miss automatically and pre-v2.5.0 entries are never read again (they stay in the file, unused).
|
|
86
|
+
|
|
87
|
+
### Fixed — detection accuracy
|
|
88
|
+
|
|
89
|
+
- **Flat Spring Initializr layout (`com/example/demo/controller/*.java`) is Pattern C, not "Pattern B with one domain named `demo`".** The Pattern B glob `**/*/controller/*.java` always matched the root package's last segment, making Pattern C unreachable for every real Java project. A path counts as flat only when its layer dir is a direct child of the base package **and** no layer-class stem under it starts with that package segment — so single-domain domain-first projects (`payment/controller/PaymentController.java`) stay Pattern B. The Pattern C test now asserts exact domain names instead of `>= 1`. The stem test looks at *every* layer class under the base package (controller, service, mapper, repository, dao, dto), so a single-domain project such as `account/{controller/LoginController, service/AccountService, dto/LoginDto}` stays Pattern B `account` because `AccountService` is named after the package; a `*Application.java` sitting directly in the base package is a positive flat signal on its own (Initializr places it there). Inside a Gradle/Maven module the module's own sub-package (`api/src/main/java/com/example/api/controller/`) is treated like the Initializr base package, so `com.example.api` / `com.example.core` modules yield `user` / `order` rather than a pseudo-domain named after the module. The directory holding the Spring Boot main class (`*Application.java`) is always a base package regardless of the 4-segment `rootPackage` cap, so a 5-segment Initializr base package (`kr/co/<org>/<proj>/<app>`) is classified flat as well. *Known limit:* a single-domain project in which *no* layer class shares the package name (`auth/{controller/LoginController, service/TokenService}`) is read as flat and yields `login` / `token`. Controllers the flat test skips in a tree that another pattern claims are re-attached by class name (Pattern C entries alongside the Pattern B/D/E ones).
|
|
90
|
+
- **Java multi-module Gradle.** Root `build.gradle` with `id 'org.springframework.boot' … apply false` (plugin id, no `spring-boot` coordinate) is now recognized; when the root declares nothing, sub-module `build.gradle{,.kts}` files (≤30) are swept for the Java plugin / Spring Boot coordinates, framework version and Java version. `scan-java` discovers every `[<module>/]src/main/{java,resources}` root once and anchors all patterns there, so `api/src/main/java/...` + `core/src/main/java/...` are scanned by the primary A–E patterns instead of the coarse fallback. Single-module scan time is unchanged (slightly faster); multi-module pays a per-module multiple for full-fidelity results. Source-root discovery ignores `src/test/**` (test-fixture projects such as `src/test/resources/projects/demo/src/main/java/…`) and `buildSrc/`, so neither becomes a module.
|
|
91
|
+
- **Legacy Java version literals.** `sourceCompatibility = '1.8'` produced `languageVersion: "1"`; `<java.version>1.8</java.version>` produced `null`. Both now yield `"8"` (`normalizeJavaVersion`), across Gradle literal / ext-variable / Maven property paths.
|
|
92
|
+
- **Next.js App Router route groups.** `app/(marketing)/about/`, `app/(shop)/(nested)/checkout/` were skipped outright (`name.startsWith("(")`), so route-group-organized apps came back with zero route domains. Groups are expanded (up to 3 levels) and their children evaluated like top-level route folders; Fallback A steps over group segments the same way. The same leaf under different groups (`(shop)/settings` and `(admin)/settings`) are distinct features: colliding leaves are qualified with their group path (`shop-settings`, `admin-settings`) so domain names stay unique; non-colliding leaves keep the bare name. If names still collide after group qualification (leaves that differ only before `pages`: `src/mobile/pages/home` vs `src/desktop/pages/home`), every non-structural path segment is used (`mobile-home`, `desktop-home`). A colliding leaf with no `app`/`pages` anchor (`src/views/home` beside `src/pages/home`) is qualified with its immediate parent (`views-home`, `pages-home`), never with the whole path.
|
|
93
|
+
- **Kotlin version catalog** with a `kotlin = "x.y.z"` version entry or a Kotlin *plugin* coordinate (`org.jetbrains.kotlin.jvm` / `.plugin.*` / `.multiplatform` / `.android` / `.kapt`, `kotlin-gradle-plugin`) now overrides the Java default set by the root plugin id. Library coordinates such as `org.jetbrains.kotlin:kotlin-stdlib` are deliberately *not* a signal — Java projects pin them to settle transitive version conflicts. Independently of the catalog, a "kotlin" keyword anywhere in a build file (`buildSrc/build.gradle.kts` with `kotlin-dsl`, a `kotlin = …` version pin) never flips a project whose source tree contains `.java` files and no `.kt` files — the Kotlin scanner would find zero domains and `init` would abort; `buildSrc/` is also excluded from the sub-module build-file sweep. `build-logic/` and `gradle/plugins/` (Gradle's documented `buildSrc` replacements for Kotlin-DSL convention plugins) are excluded from the source-language evidence as well.
|
|
94
|
+
- **Python framework / ORM keywords are matched case-insensitively.** `pip freeze` and PyPI canonical names are capitalized (`Django==5.0`, `Flask==3.0`, `SQLAlchemy==2.0`); the case-sensitive `includes()` never recognized Django or Flask from `requirements.txt`, and a standard `django-admin startproject` layout then aborted `init` with `domain-groups.json has invalid totalGroups: 0`. Present since v1.x.
|
|
95
|
+
- **Kotlin package-by-feature layout without layer folders** (`com/acme/user/UserController.kt`, `UserService.kt` — the idiomatic Kotlin/Spring layout) produced zero domains and aborted `init`. The single-module fallback now derives the domain from the feature package that holds layer-suffixed classes, or from the class-name stem when the directory is a flat root/app package. Pre-existing since the Kotlin scanner was added. The fallback also runs for files the layer-folder scan did not cover, so a mixed tree (`user/controller/UserController.kt` + `order/OrderController.kt`) keeps both domains; in that mixed case only feature packages named after their classes are accepted (a stray `SomeHandler.kt` in the root package is not promoted). A `dto/` / `vo/` / `entity/` folder sitting directly under the root package no longer manufactures a domain named after the root package (`com/acme/dto/UserDto.kt` → `acme`) that would both survive and force the strict mode; the artifact is dropped when real domains exist.
|
|
96
|
+
- **Layer-first Express / Fastify / Koa** (`src/controllers/`, `src/routes/`, `src/services/`) no longer reports the layer folders themselves as domains. When every candidate folder under `src/` is a layer name, domains are derived from file stems (`user.controller.js`, `users.routes.ts`, `orderService.js` → `user`, `order`; `pattern: "layer-first"`). A plural stem is folded into its singular twin only when *both* exist (`users.routes.ts` + `user.controller.js` → `user`; `-ies`→`-y`, `-es`, `-s`); a lone plural is kept verbatim, never guessed. Feature-first layouts (`src/<domain>/`) and NestJS modules are unchanged. The layer-first path is taken as soon as two layer folders exist; other non-infrastructure siblings (`src/jobs/`, `src/billing/`) become whole-folder feature domains instead of disabling the path (which previously resurrected `controllers` / `routes` as domains). `db/`, `migrations/`, `scripts/`, `public/`, `tests/` and similar are treated as infrastructure. The layer-first path additionally requires a routing layer folder (`controllers/`, `routes/`, `handlers/`, `api/`) at the top: module-first NestJS trees that keep shared `entities/` + `dtos/` next to `src/users/`, `src/orders/` stay module-first (no `users` → `user` rename, no one-file stem domains), and layer-named folders are never emitted as domains by the directory fallback either. Layer/role suffixes are stripped repeatedly and only after a separator or as a PascalCase word (`email.service.impl.ts` → `email`, `prototype.ts` stays `prototype`), and a file's role comes from its layer folder rather than from substrings of its path.
|
|
97
|
+
- **Layer-first FastAPI / Flask** (`app/routers/users.py`, `app/models/user.py`, `app/schemas/`) — same treatment and the same plural folding on the Python side when `app/` holds only layer folders. Feature packages that sit next to the layer folders (`src/routers/users.py` + `src/tasks/billing.py`) become domains as well instead of being dropped, and the layer/feature folders are collected from the same parent so a `src/` frontend tree is never mistaken for Python features. Virtualenvs and `node_modules` under `src/` or `app/` (`env/`, `venv/`, `.venv/`, `virtualenv/`, `site-packages/`) are ignored so they are never scanned as feature packages.
|
|
98
|
+
- **Frontend in a sub-directory.** A backend repo with the SPA in `frontend/`, `client/`, `web/`, `ui/`, `webapp/` or `front/` (own `package.json`, no root `package.json`) now detects the frontend from there (`stack.frontendRoot`) and the frontend scanner is rooted at that directory. Detection uses the same dependency precedence as the root (`next` → `@angular/core` → `nuxt` → `react` → `vue`) plus a config-file fallback (`next.config.*`, `vite.config.*`, `nuxt.config.*`, `angular.json`). Previously such repos were scanned as backend-only. `.claudeos-scan.json` is still read from the *project* root (where it is documented to live), not from the sub-directory, so `frontendScan` overrides keep working for sub-directory SPAs. A repo whose *only* application is the sub-directory SPA is no longer reported as "no language detected": `language` (TypeScript when `typescript` / `tsconfig.json` is present), `languageVersion` and `packageManager` (pnpm / yarn / bun / npm lockfile) are filled from the sub-directory, with backend values taking precedence when a backend exists. The sub-directory's own `.env*` is read as `stack.frontendEnvInfo` (same redaction and masking) and both it and `stack.frontendPort` are projected into `pass3-context.json`. The SPA's language / package manager are filled only after every backend block has run, so a Django / FastAPI / Flask backend with `frontend/package.json` stays `language: python` and keeps its package manager; the structure scanner also dispatches Python frameworks to the Python scanner regardless of `language`, so a Django repo whose root `package.json` exists only for Tailwind/PostCSS tooling is never handed to the Node scanner. A root `package.json` that exists only for tooling (Tailwind/PostCSS/ESLint) no longer makes a Django / FastAPI / Flask repo report `language: typescript` in `project-analysis.json` and CLAUDE.md §2: when a Python manifest is present and no Node backend framework is, `language` is reclaimed as `python` (a NestJS/Express/Fastify framework keeps Node).
|
|
99
|
+
- **Backend port and frontend dev-server port are resolved separately.** The single default chain (`stack.frontend === "angular" ? 4200 …`) handed a Spring/Django backend the Angular/Next dev-server port whenever a SPA lived beside it. `stack.port` is now the backend's port when a backend exists (`.env*` → framework default) and `stack.frontendPort` is the SPA's dev-server port whenever a frontend exists (sub-directory `.env*` → root `.env*` `PORT` for a root SPA → Vite 5173 / Angular 4200 / 3000). For a frontend-only project the two agree. When a single root `.env*` carries both (`SERVER_PORT=8080` + `VITE_PORT=3000`), the frontend-prefixed key (`VITE_*` / `NEXT_*` / `NUXT_*` / `NG_*`) feeds `frontendPort` and only the backend key feeds `stack.port` — previously `VITE_PORT` won the shared `extractPort()` precedence and was documented as the backend's port. `stack.envInfo.port` (what Pass 3 reads for the Server Port row) is set to the backend value under the same rule, with the frontend value exposed as `envInfo.frontendPort`; `plan-installer` uses the same backend definition as the detector (a JVM/Python project is a backend even without a recognized framework), so a plain Maven project beside a Vite SPA keeps 8080.
|
|
100
|
+
- **React + Vite next to a backend picks the Vite template.** Template selection used `stack.framework === "vite"`, which is impossible when Spring/Django owns `framework`, so a Spring + `frontend/` (React + Vite) repo was handed the Next.js frontend template. The bundler is now recorded separately (`stack.frontendBundler = "vite"` — root `package.json`, monorepo workspace packages whose deps are merged into the root view, or the sub-directory; from the `vite` dependency or the `vite.config.*` fallback) and `selectTemplates()` honors it. This also changes monorepos such as `apps/api` (NestJS) + `apps/web` (React + Vite), which previously received the Next.js frontend template.
|
|
101
|
+
- **`stack.frontendRoot` reaches every prompt.** `prompt-generator` prepends a `Frontend source root: {{PROJECT_ROOT}}/<dir>/` note to the shared header when the SPA lives in a sub-directory, so Pass 1/2/3 read the stack templates' `app/…` / `src/…` examples relative to that directory instead of the repo root; `pass3-context.json` carries `frontendRoot` / `frontendBundler` in its slim `stack` block.
|
|
102
|
+
- **Zero-domain abort is explained.** `init` still stops when the scanner finds no domains (there is nothing for Pass 1-3 to analyze and writing an empty CLAUDE.md would be worse), but the message now states the detected stack, lists the layouts the scanner recognizes, points at `project-analysis.json`, and confirms nothing was written — instead of `domain-groups.json has invalid totalGroups: 0`. Under `--force` / "fresh" the message no longer claims nothing was touched: it states that the managed `.claude/rules/NN.*` categories and `generated/` pass files had already been removed and points at version control.
|
|
103
|
+
|
|
104
|
+
### Fixed — validator coverage
|
|
105
|
+
|
|
106
|
+
- **`STALE_PATH` covers JVM / Python / SFC / MyBatis paths.** `content-validator [10/10]` matched only `src/….{ts,tsx,js,jsx}`, so Java, Kotlin and Python projects had zero path-claim coverage despite "no invented paths" being the headline guarantee. Extensions added: `mjs cjs vue svelte java kt kts py xml sql` (config extensions deliberately excluded — `application-{profile}.yml` is illustrative far more often than a claim). `resolvePathClaim` resolves `src/…` claims against any module directory up to three levels deep that contains `src/` (memoized), not only `apps/*` / `packages/*`, so multi-module citations like `api/src/main/java/…` and the nested Kotlin CQRS layout the scanner itself supports (`servers/query/<x>/src/main/kotlin/…`) are not false positives. A module-qualified citation (`core/src/main/java/…`, `apps/web/src/…`) is captured whole and checked at that exact location only, so citing the wrong module is flagged instead of being rescued by the bare-`src/` module search; virtualenvs (`venv/`, `.venv/`, `env/`, `site-packages/`), `vendor/`, `docs/`, `tools/`, `scripts/`, `test(s)/` and `fixtures/` are never treated as module roots, so a hallucinated `src/…` path that happens to exist under `<venv>/src` is still reported. A module prefix is captured only when it starts a path token, so a package import such as `@acme/ui/src/Button.tsx` is not read as module `acme/ui` (its `src/Button.tsx` tail still resolves through the workspace search), `libsrc/x.ts` never yields a `src/x.ts` claim, and `node_modules/<pkg>/src/…` mentions are skipped as dependency references. `docs/`, `tools/`, `scripts/`, `test(s)/` and `fixtures/` are skipped as module roots only at the project root — inside a workspace container (`apps/docs`, `packages/tools`) they are legitimate packages, so the default Turborepo layout resolves as before; a dev-server URL (`localhost:5173/src/main.tsx`) never yields a `5173` module claim.
|
|
107
|
+
|
|
108
|
+
### Changed — Pass 3 allowlist is written by the orchestrator
|
|
109
|
+
|
|
110
|
+
- `## Allowed Source Paths` in `pass3a-facts.md` is now injected by `init.js` directly from `project-analysis.json` after Pass 3a (`injectAllowedPathsSection()` in `plan-installer/source-paths.js`): replaces any LLM-written section wholesale (fence-aware, heading-suffix-tolerant, duplicate sections collapsed to one, an unterminated fence at EOF is closed first), appends if absent, emits the documented fallback line when the allowlist is empty, normalizes CRLF → LF, and runs on resumed runs where 3a is skipped. Until now the list reached 3b/3c/3d only if the model hand-copied up to 500 paths out of `pass3-context.json`, and `renderAllowedPathsSection()` was never called at runtime. `pass3a-facts.md` template tells the model not to write the section. If Pass 3a is marked complete but the facts file is missing/empty, `init` now stops with a clear error instead of fabricating a facts file.
|
|
111
|
+
|
|
112
|
+
### Changed — templates & generated content
|
|
113
|
+
|
|
114
|
+
- `52.ai-work-rules.md` (static fallback): removed the `plan/` master-document rule (master plans were removed in v2.1.0); `00.standard-reference.md` guidance rewritten to agree with `pass3-footer.md` and the doc-writing guide; `build.gradle.kts` added to the manifest lists.
|
|
115
|
+
- Six stack templates no longer tell the model that `50.sync` rules should recommend `npx claudeos-core refresh` (a no-op since v2.1.0).
|
|
116
|
+
- Six stack templates (`angular`, `node-fastify`, `node-nestjs`, `node-vite`, `python-flask`, `vue-nuxt`) gained the `00.core/04.doc-writing-guide.md` forward reference in `00.standard-reference.md`.
|
|
117
|
+
- `angular/pass3.md`: orchestrator renamed `01.scaffold-page-feature.md` so its stem matches the `scaffold-page-feature/` sub-folder that `content-validator` and `ensureDirectories()` expect.
|
|
118
|
+
- `java-spring` / `kotlin-spring`: `40.infra/01.environment-config-rules.md` `paths` now include `**/*.gradle`, `**/*.gradle.kts`, `**/gradle/libs.versions.toml`, `**/pom.xml`; the linked `40.infra/01.environment-config.md` standard description in `java-spring` mentions build scripts and the version catalog accordingly.
|
|
119
|
+
- `claude-md-scaffold.md` §8 usage rule 5: periodic compaction is scoped to `failure-patterns.md` (`decision-log.md` is never compacted).
|
|
120
|
+
- `pass4.md`: "13 hallucination prevention patterns" → 17 (matches the static table); `claudeMdAppended` marker field is `false` (Pass 4 has not touched CLAUDE.md since v2.3.0) in both the template and `init.js`.
|
|
121
|
+
- `init.js` static-fallback log no longer claims "Plans scaffolded + CLAUDE.md appended".
|
|
122
|
+
|
|
123
|
+
### Docs
|
|
124
|
+
|
|
125
|
+
- README: "byte-identical output" / "Nothing is guessed" / "Same project = same output" reworded to what is actually guaranteed (fixed 8-section structure, scanner facts + Pass 1 reads, validated identically across languages); excerpt headings noted as demoted for rendering; `build.gradle.kts` listed; test badge 825.
|
|
126
|
+
- `docs/safety.md` and `docs/commands.md`: `--force` sections describe the managed-category wipe and the preserved user-owned files under `.claude/rules/`; `docs/commands.md` also scopes `memory compact` to `failure-patterns.md`.
|
|
127
|
+
- `docs/stacks.md`: `.env` section no longer claims a `DATABASE_URL` whitelist; describes credential masking. `docs/verification.md`: `STALE_PATH` row lists the widened extension set and module-directory resolution. `docs/memory-layer.md`: compaction section scoped to `failure-patterns.md`.
|
|
128
|
+
- `lint --help` lists the T1 canonical-heading and S2 content checks.
|
|
129
|
+
|
|
130
|
+
### Behavior changes (intentional)
|
|
131
|
+
|
|
132
|
+
1. `memory compact` skips `decision-log.md` (also loses the 400-line cap for that file).
|
|
133
|
+
2. `health` no longer reconciles MANIFEST ↔ §6; drift is reported as `MANIFEST_DRIFT` advisories. Run `node <tools>/manifest-generator/index.js --sync-skills` to reconcile on demand.
|
|
134
|
+
3. Flat-layout Java projects now yield one domain per `*Controller` class instead of a single package-named domain; a flat controller next to domain-first packages (`demo/controller/HomeController.java` beside `demo/user/controller/…`) no longer produces a bogus package-named domain — it becomes its own class-name domain (`home`, Pattern C) so it is never silently left out of every domain.
|
|
135
|
+
4. Kotlin `build.gradle.kts` projects declaring `id("org.springframework.boot")` are now `framework: spring-boot`, which activates `10.backend` output (previously `framework: null`).
|
|
136
|
+
5. React + Vite frontends that share a repo with a backend (root `package.json`, monorepo workspace, or `frontend/`-style sub-directory) are now generated from the `node-vite` template instead of `node-nextjs`. Re-run `npx claudeos-core init --force` once to regenerate the frontend standards/rules under the Vite template.
|
|
137
|
+
|
|
138
|
+
### Migration
|
|
139
|
+
|
|
140
|
+
- Existing projects: nothing to do for the memory / health / `.env` fixes — they take effect on the next command.
|
|
141
|
+
- Flat-layout Java projects and route-group Next.js projects: re-run `npx claudeos-core init --force` to regenerate per-domain files under the corrected domain set; a plain resume keeps the old files (Rule B) and adds the new ones alongside.
|
|
142
|
+
- Java / Python projects may see new `STALE_PATH` advisories for `.java` / `.py` / `.xml` paths the model invented; they are advisory-tier and do not fail `init` or `health`.
|
|
143
|
+
- All 10 `README.{lang}.md` files and all 9 localized `docs/{lang}/{commands,safety,stacks,verification,memory-layer}.md` sets carry the v2.5.0 wording (`memory compact` scope, managed-category `--force` wipe, credential masking, widened `STALE_PATH` coverage).
|
|
144
|
+
|
|
145
|
+
### Tests
|
|
146
|
+
|
|
147
|
+
- 89 new: flat-layout / Initializr detection, multi-module source roots, Java 8 (`1.8`) Gradle + Maven, multi-module Gradle detection ×2, route groups, allowlist injection ×8 (append / replace / empty fallback / fenced heading / heading suffix / CRLF / unclosed fence / duplicate sections), multi-module `STALE_PATH` resolution ×2, `wipeManagedRuleCategories` ×2, case-insensitive Python framework detection ×2, Kotlin package-by-feature fallback ×2, `kotlin-stdlib`-only catalog stays Java, layer-first Express / FastAPI ×2, frontend sub-directory ×6 (dependency, `vite.config` fallback, Nuxt, root React + Vite bundler, scanner rooting, `pass3-context` projection), Vite template selection with a backend present, `Frontend source root` prompt note, `memory compact` leaves `decision-log.md` byte-identical, content-hashed translation cache ×4 (incl. a mocked real translation proving the cache is written), credential masking with `@` / `/` passwords and scheme-less DSNs, Java-only source tree vs kotlin keywords ×3, single-domain Pattern B preservation, `*Application.java` flat signal, per-module packages, `src/test` / `buildSrc` exclusion, Kotlin mixed layout, Python mixed layer/feature layout, sub-directory SPA overrides from the project root, route-group leaf collision, 3-level module `STALE_PATH` resolution, `manifest-generator` skills-sync gating ×3 (plain run read-only / `--sync-skills` / an exported `CLAUDEOS_SKILLS_SYNC` does nothing), `@` in URL path / query / `mailto` never rewritten, flat controller re-attached beside Pattern B, Kotlin flat-root `dto/` artifact ×2, Express mixed layer + feature folders, Python virtualenv under `src/`, leaf collision before `pages`, root-level `NN.` file survives `--force`, SPA-only sub-directory repo ×2 (language / package manager / env facts, backend precedence), module-qualified `STALE_PATH` claims ×3, `frontendPort` / `frontendEnvInfo` projection, backend vs frontend port resolution ×3, Django + `frontend/` stays Python ×2, module-first NestJS with shared `entities/` + `dtos/`, root `.env` `VITE_PORT` beside `SERVER_PORT`, package-import / `libsrc` / `node_modules` non-claims ×2, `--force` zero-domain message, `build-logic/` Kotlin convention plugins, tooling-only root `package.json` in a Django repo, NestJS + stray `requirements.txt`, `SERVER_PORT` + `VITE_PORT` in one `.env` (envInfo.port), framework-less Maven + Vite SPA ports, `*.service.impl.ts` / `prototype.ts` stems, 5-segment base package via `*Application.java`, `views/` vs `pages/` leaf collision, Turborepo `apps/docs` + `packages/tools` resolution, dev-server URL non-claim, connection-parameter credential masking.
|
|
148
|
+
- Replaced: `redactSensitiveVars preserves DATABASE_URL` → masking assertions; inline `--force` wipe reproduction → managed-category assertion. Pass 4 marker fixtures now use `claudeMdAppended: false` to match the template.
|
|
149
|
+
|
|
150
|
+
- **Files changed** — `bin/commands/{init,memory,lint}.js`, `lib/{env-parser,memory-scaffold}.js`, `plan-installer/{stack-detector,structure-scanner,domain-grouper,prompt-generator,pass3-context-builder,source-paths,index}.js`, `plan-installer/scanners/{scan-java,scan-kotlin,scan-node,scan-python,scan-frontend}.js`, `content-validator/index.js`, `manifest-generator/index.js`, 15 templates under `pass-prompts/templates/` (3 common + 12 stack), `README.md` + 9 localized `README.{lang}.md`, `docs/{safety,commands,stacks,verification,memory-layer,manual-installation}.md` (+ the same 5 docs in 9 localized `docs/{lang}/` sets, + 9 localized `manual-installation.md` version strings), 15 test files, `package.json` / `package-lock.json` (`2.4.4` → `2.5.0`).
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
29
154
|
## [2.4.4] — 2026-05-04
|
|
30
155
|
|
|
31
156
|
Documentation-only release. Translation polish across 8 non-Korean language `docs/{lang}/` directories. Test suite remains 736 / 736 pass.
|
package/README.de.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
4
4
|
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
5
|
-
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
@@ -25,7 +25,7 @@ Claude Code greift bei jeder neuen Session auf die Standardwerte des jeweiligen
|
|
|
25
25
|
|
|
26
26
|
**ClaudeOS-Core erzeugt diese Regeln deterministisch neu, direkt aus dem Quellcode.** Zuerst liest ein Node.js-Scanner alles aus: Stack, ORM, Paket-Layout und Dateipfade. Anschließend schreibt eine 4-Pass-Pipeline mit Claude den kompletten Dokumentensatz: `CLAUDE.md`, die automatisch geladenen `.claude/rules/`, dazu Standards und Skills. Eine explizite Pfad-Allowlist hält das LLM dabei in der Spur, aus ihr kann es nicht ausbrechen. Fünf Validatoren prüfen das Ergebnis, bevor es ausgeliefert wird.
|
|
27
27
|
|
|
28
|
-
Das Resultat: Bei gleichem Input bekommst du
|
|
28
|
+
Das Resultat: Bei gleichem Input bekommst du dieselbe `CLAUDE.md` mit 8 festen Sections, geprüft durch dieselben 25 strukturellen Checks in jeder der 10 Sprachen, und jeder zitierte Quellpfad wird gegen die Festplatte verifiziert. (Details findest du weiter unten unter [Was es anders macht](#was-es-anders-macht).)
|
|
29
29
|
|
|
30
30
|
Für langlebige Projekte legt das Tool außerdem einen separaten [Memory Layer](#memory-layer-optional-für-langlebige-projekte) an.
|
|
31
31
|
|
|
@@ -115,7 +115,7 @@ Hier ein Lauf auf [`spring-boot-realworld-example-app`](https://github.com/gothi
|
|
|
115
115
|
</details>
|
|
116
116
|
|
|
117
117
|
<details>
|
|
118
|
-
<summary><strong>Was tatsächlich in deiner <code>CLAUDE.md</code> landet (echter Auszug, Section 1 + 2)</strong></summary>
|
|
118
|
+
<summary><strong>Was tatsächlich in deiner <code>CLAUDE.md</code> landet (echter Auszug, Section 1 + 2; Überschriften für die README-Darstellung auf <code>####</code> herabgestuft, die echte Datei verwendet <code>## N.</code>)</strong></summary>
|
|
119
119
|
|
|
120
120
|
```markdown
|
|
121
121
|
# CLAUDE.md — spring-boot-realworld-example-app
|
|
@@ -148,7 +148,7 @@ an XML-driven MyBatis persistence layer and JWT-based authentication.
|
|
|
148
148
|
| Test Stack | JUnit Jupiter 5, Mockito, AssertJ, rest-assured, spring-mock-mvc |
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Die Stack-Zeilen (Java 11, Spring Boot 2.6.3, Gradle, MyBatis, SQLite, Port 8080) stammen aus dem deterministischen Scanner. Die feineren Details, also die exakten Dependency-Koordinaten, der Dateiname `dev.db`, der Migrationsname `V1__create_tables.sql` und das "no JPA", liest Pass 1 aus `build.gradle`, `application.properties` und dem Source-Tree, wobei die Scanner-Fakten als Leitplanken dienen; anschließend prüfen die Validatoren sie gegen. Aus Framework-Defaults stammt hier nichts.
|
|
152
152
|
|
|
153
153
|
</details>
|
|
154
154
|
|
|
@@ -309,7 +309,7 @@ Kategorien mit demselben Nummern-Präfix in `rules/` und `standard/` decken dens
|
|
|
309
309
|
| Rolle | Schmerzpunkt, der wegfällt |
|
|
310
310
|
|---|---|
|
|
311
311
|
| **Solo-Entwickler**, der ein neues Projekt mit Claude Code startet | "Claude in jeder Session die eigenen Konventionen erklären" entfällt komplett. `CLAUDE.md` und die `.claude/rules/` mit acht Kategorien entstehen in einem einzigen Durchlauf. |
|
|
312
|
-
| **Team-Lead**, der gemeinsame Standards über mehrere Repos pflegt | `.claude/rules/` driften, sobald jemand Pakete umbenennt, das ORM tauscht oder den Response-Wrapper anpasst. ClaudeOS-Core
|
|
312
|
+
| **Team-Lead**, der gemeinsame Standards über mehrere Repos pflegt | `.claude/rules/` driften, sobald jemand Pakete umbenennt, das ORM tauscht oder den Response-Wrapper anpasst. ClaudeOS-Core generiert gegen ein festes Scaffold mit 8 Sections neu: gleiche Struktur in jedem Repo, gleiches Validator-Urteil, sodass Diffs Konventionsänderungen zeigen statt Layout-Rauschen. |
|
|
313
313
|
| **Du nutzt Claude Code bereits**, hast aber genug davon, generierten Code immer wieder zu reparieren | Falscher Response-Wrapper, falsches Paket-Layout, JPA, obwohl ihr MyBatis fahrt, verstreute `try/catch`-Blöcke trotz zentraler Middleware. Der Scanner liest die echten Konventionen aus, und jeder Claude-Pass läuft gegen eine explizite Pfad-Allowlist. |
|
|
314
314
|
| **Onboarding in ein neues Repo** (Bestandsprojekt, neues Team) | Einmal `init` im Repo ausführen, und du hast eine lebendige Architekturkarte: Stack-Tabelle in der CLAUDE.md, Rules pro Layer mit ✅/❌-Beispielen, dazu ein Decision-Log mit dem "Warum" hinter den großen Entscheidungen wie JPA gegen MyBatis oder REST gegen GraphQL. Fünf Dateien lesen schlägt 5.000 Quelldateien wälzen. |
|
|
315
315
|
| **Du arbeitest auf Koreanisch, Japanisch, Chinesisch oder in 7 weiteren Sprachen** | Die meisten Rule-Generatoren für Claude Code beherrschen nur Englisch. ClaudeOS-Core schreibt das vollständige Set in **10 Sprachen** (`en/ko/ja/zh-CN/es/vi/hi/ru/fr/de`). Die strukturelle Validierung bleibt dabei byte-identisch: Der `claude-md-validator` urteilt unabhängig von der Ausgabesprache gleich. |
|
|
@@ -331,7 +331,7 @@ Hier: Code reads your stack → Code passes confirmed facts to Claude → C
|
|
|
331
331
|
|
|
332
332
|
Die Pipeline läuft in **drei Phasen**; vor und nach dem LLM-Aufruf übernimmt jeweils Code:
|
|
333
333
|
|
|
334
|
-
**1. Step A — Scanner (deterministisch, ohne LLM).** Ein Node.js-Scanner durchläuft den Projekt-Root, liest `package.json`, `build.gradle`, `pom.xml` und `pyproject.toml`, parst `.env*`-Dateien und maskiert dabei sensible Variablen wie `PASSWORD/SECRET/TOKEN/JWT_SECRET/...`. Anschließend klassifiziert er das Architekturmuster (5 Java-Patterns A/B/C/D/E, Kotlin CQRS oder Multi-Module, Next.js App- gegenüber Pages-Router, FSD, Components-Pattern), erkennt Domänen und baut eine explizite Allowlist aller tatsächlich existierenden Quellpfade. Das Ergebnis landet in `project-analysis.json`, der Single Source of Truth für alles, was danach kommt.
|
|
334
|
+
**1. Step A — Scanner (deterministisch, ohne LLM).** Ein Node.js-Scanner durchläuft den Projekt-Root, liest `package.json`, `build.gradle`, `build.gradle.kts`, `pom.xml` und `pyproject.toml`, parst `.env*`-Dateien und maskiert dabei sensible Variablen wie `PASSWORD/SECRET/TOKEN/JWT_SECRET/...`. Anschließend klassifiziert er das Architekturmuster (5 Java-Patterns A/B/C/D/E, Kotlin CQRS oder Multi-Module, Next.js App- gegenüber Pages-Router, FSD, Components-Pattern), erkennt Domänen und baut eine explizite Allowlist aller tatsächlich existierenden Quellpfade. Das Ergebnis landet in `project-analysis.json`, der Single Source of Truth für alles, was danach kommt.
|
|
335
335
|
|
|
336
336
|
**2. Step B — 4-Pass-Pipeline mit Claude (gebunden an die Fakten aus Step A).**
|
|
337
337
|
- **Pass 1** liest pro Domain-Gruppe repräsentative Dateien und extrahiert dort jeweils etwa 50 bis 100 Konventionen: Response-Wrapper, Logging-Bibliotheken, Error-Handling, Naming-Konventionen, Test-Patterns. Der Pass läuft einmal pro Domain-Gruppe (`max 4 domains, 40 files per group`), dadurch läuft der Kontext nie über.
|
|
@@ -358,12 +358,14 @@ Pro-Pass-Details, Marker-basiertes Resume, der Staged-Rules-Workaround für die
|
|
|
358
358
|
|
|
359
359
|
12 Stacks, automatisch aus deinen Projektdateien erkannt:
|
|
360
360
|
|
|
361
|
-
**Backend:** Java/Spring Boot · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
361
|
+
**Backend:** Java/Spring Boot (sowie Spring Framework ohne Boot, siehe unten) · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
362
362
|
|
|
363
363
|
**Frontend:** Node/Next.js · Node/Vite · Angular · Vue/Nuxt
|
|
364
364
|
|
|
365
365
|
Auch Multi-Stack-Projekte funktionieren ohne Zusatzaufwand, etwa ein Spring-Boot-Backend zusammen mit einem Next.js-Frontend.
|
|
366
366
|
|
|
367
|
+
**Legacy-Java ist ein vollwertiges Ziel (v2.5.1).** Spring 1.x–6.x ohne Boot wird erkannt: Gradle (`apply plugin:`-Ära, `group:/name:/version:`-Notation in beliebiger Reihenfolge, Auflösung über `gradle.properties` / `apply from:` / buildSrc, Version Catalogs, Roots mit nur `settings.gradle`), Maven (Maven-2-POMs, `${spring.version}`-Properties, `spring-framework-bom`, Multi-Module-Roots, Schwesterprojekte ohne Root-POM), **Ant + Ivy**, **Metadaten von Eclipse / IntelliJ / NetBeans** (`.classpath` inklusive Verweisen auf nicht eingecheckte JARs, Compliance-Level aus `.settings`, `.idea/misc.xml`, `nbproject`), `WebContent/WEB-INF/lib/**/*.jar`, `WEB-INF/web.xml`, Spring-XSD-Schemaversionen und **eGovFrame** — mit Framework, Spring-Framework-Version, Java-Level, `war`/`ear`-Packaging, JDBC-Treiber und ORM. Quellbäume mit `src/`-Root werden mit denselben Domain-Mustern gescannt wie `src/main/java`. Jede gemeldete Version stammt aus einer Build-Datei, einem JAR-Namen oder einer im Projekt definierten Property; nichts wird aus Framework-Defaults abgeleitet. JVM-Projekte ganz ohne Spring (`java-library`, `application`, reines Servlet-`war`) werden als Java mit `framework: null` gemeldet, niemals als Spring.
|
|
368
|
+
|
|
367
369
|
Erkennungsregeln und die Felder, die jeder Scanner extrahiert, beschreibt [docs/de/stacks.md](docs/de/stacks.md).
|
|
368
370
|
|
|
369
371
|
---
|
|
@@ -393,7 +395,7 @@ Die meisten Documentation-Tools für Claude Code generieren aus einer Beschreibu
|
|
|
393
395
|
|
|
394
396
|
Daraus folgen drei konkrete Effekte:
|
|
395
397
|
|
|
396
|
-
1. **Deterministische Stack-Erkennung.** Gleiches Projekt plus gleicher Code ergibt
|
|
398
|
+
1. **Deterministische Stack-Erkennung und Struktur.** Gleiches Projekt plus gleicher Code ergibt dasselbe Scan-Ergebnis und dasselbe `CLAUDE.md`-Layout mit 8 Sections. Die Formulierungen innerhalb der Sections schreibt weiterhin das LLM; festgelegt sind die Fakten, die es bekommt, und die Form, die es ausfüllen muss.
|
|
397
399
|
2. **Keine erfundenen Pfade.** Der Pass-3-Prompt führt jeden erlaubten Quellpfad explizit auf, deshalb kann Claude nichts zitieren, was im Code nicht existiert.
|
|
398
400
|
3. **Multi-Stack-fähig.** Backend- und Frontend-Domänen verwenden im selben Lauf jeweils eigene Analyse-Prompts.
|
|
399
401
|
|
|
@@ -429,7 +431,7 @@ Was jeder einzelne Validator im Detail prüft, beschreibt [docs/de/verification.
|
|
|
429
431
|
|
|
430
432
|
Vier Dateien, alle von Pass 4 geschrieben:
|
|
431
433
|
|
|
432
|
-
- `decision-log.md`: Append-only-Log nach dem Muster "warum X statt Y", geseedet aus `pass2-merged.json
|
|
434
|
+
- `decision-log.md`: Append-only-Log nach dem Muster "warum X statt Y", geseedet aus `pass2-merged.json` (wird nie komprimiert).
|
|
433
435
|
- `failure-patterns.md`: wiederkehrende Fehler mit Frequenz- und Importance-Scores.
|
|
434
436
|
- `compaction.md`: wie der Memory-Bereich im Lauf der Zeit automatisch komprimiert wird.
|
|
435
437
|
- `auto-rule-update.md`: Patterns, die später eigene Rules werden sollten.
|
|
@@ -437,7 +439,7 @@ Vier Dateien, alle von Pass 4 geschrieben:
|
|
|
437
439
|
Zwei Befehle pflegen diesen Layer auf Dauer:
|
|
438
440
|
|
|
439
441
|
```bash
|
|
440
|
-
# Failure-Patterns-Log komprimieren (regelmäßig ausführen)
|
|
442
|
+
# Failure-Patterns-Log komprimieren (regelmäßig ausführen; decision-log.md bleibt unangetastet)
|
|
441
443
|
npx claudeos-core memory compact
|
|
442
444
|
|
|
443
445
|
# Häufige Failure-Patterns zu Rule-Vorschlägen befördern
|
package/README.es.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
4
4
|
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
5
|
-
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
@@ -25,7 +25,7 @@ Cada vez que abres una sesión nueva, Claude Code vuelve a los valores genérico
|
|
|
25
25
|
|
|
26
26
|
**ClaudeOS-Core las regenera de forma reproducible a partir del código real.** Primero entra un scanner en Node.js que reconoce el stack, el ORM, la organización de paquetes y las rutas de archivos. A continuación, la pipeline de 4 pasos sobre Claude escribe el conjunto completo: `CLAUDE.md`, las reglas auto-cargadas en `.claude/rules/`, los estándares y las skills. Todo queda acotado por una lista explícita de rutas permitidas que el modelo no puede saltarse. Y para cerrar, cinco validators revisan el resultado antes de darlo por bueno.
|
|
27
27
|
|
|
28
|
-
De este modo, la misma entrada produce siempre la misma
|
|
28
|
+
De este modo, la misma entrada produce siempre la misma estructura de `CLAUDE.md` en 8 secciones, validada por las mismas 25 comprobaciones estructurales en cualquiera de los 10 idiomas disponibles, y cada ruta de código citada se verifica contra el disco. Más abajo se entra en detalle en [Qué lo hace diferente](#qué-lo-hace-diferente).
|
|
29
29
|
|
|
30
30
|
Para proyectos de larga duración, además se prepara una [Memory Layer](#memory-layer-opcional-para-proyectos-de-larga-duración) aparte.
|
|
31
31
|
|
|
@@ -115,7 +115,7 @@ Lo ejecutamos sobre [`spring-boot-realworld-example-app`](https://github.com/got
|
|
|
115
115
|
</details>
|
|
116
116
|
|
|
117
117
|
<details>
|
|
118
|
-
<summary><strong>Lo que acaba en tu <code>CLAUDE.md</code> (extracto real, secciones 1 y 2)</strong></summary>
|
|
118
|
+
<summary><strong>Lo que acaba en tu <code>CLAUDE.md</code> (extracto real, secciones 1 y 2; los encabezados se rebajan a <code>####</code> para que se rendericen en el README, el archivo real usa <code>## N.</code>)</strong></summary>
|
|
119
119
|
|
|
120
120
|
```markdown
|
|
121
121
|
# CLAUDE.md — spring-boot-realworld-example-app
|
|
@@ -148,7 +148,7 @@ an XML-driven MyBatis persistence layer and JWT-based authentication.
|
|
|
148
148
|
| Test Stack | JUnit Jupiter 5, Mockito, AssertJ, rest-assured, spring-mock-mvc |
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Las filas del stack (Java 11, Spring Boot 2.6.3, Gradle, MyBatis, SQLite, puerto 8080) salen del scanner determinista. Los detalles más finos, desde las coordenadas exactas de cada dependencia hasta el nombre del archivo `dev.db`, el de la migración `V1__create_tables.sql` o ese «no JPA», los lee Pass 1 en `build.gradle`, `application.properties` y el árbol de fuentes, tomando los hechos del scanner como restricción, y después los validators los contrastan. Nada sale de los valores por defecto del framework.
|
|
152
152
|
|
|
153
153
|
</details>
|
|
154
154
|
|
|
@@ -309,7 +309,7 @@ Las categorías que comparten prefijo numérico entre `rules/` y `standard/` rep
|
|
|
309
309
|
| Si eres... | El dolor que quita |
|
|
310
310
|
|---|---|
|
|
311
311
|
| **Dev en solitario** que arranca un proyecto con Claude Code | Se acabó lo de explicarle a Claude las convenciones del proyecto en cada sesión. Generas `CLAUDE.md` y las 8 categorías de `.claude/rules/` de una sola pasada. |
|
|
312
|
-
| **Tech lead** manteniendo estándares compartidos entre repos | Cuando alguien renombra paquetes, cambia de ORM o retoca el wrapper de respuesta, `.claude/rules/` deja de cuadrar. ClaudeOS-Core lo
|
|
312
|
+
| **Tech lead** manteniendo estándares compartidos entre repos | Cuando alguien renombra paquetes, cambia de ORM o retoca el wrapper de respuesta, `.claude/rules/` deja de cuadrar. ClaudeOS-Core lo regenera contra un scaffold fijo de 8 secciones: misma estructura en todos los repos y mismo veredicto del validator, así que el diff muestra cambios de convención y no ruido de maquetación. |
|
|
313
313
|
| **Ya usas Claude Code** y estás cansado de corregir lo que genera | Wrapper equivocado, paquetes mal organizados, JPA cuando se usa MyBatis, `try/catch` esparcidos cuando ya hay middleware central. El scanner extrae las convenciones reales y cada paso de Claude se ejecuta contra una lista explícita de rutas permitidas. |
|
|
314
314
|
| **Te incorporas a un repo nuevo** (proyecto existente, llegada al equipo) | Lanzas `init` sobre el repo y obtienes un mapa vivo de la arquitectura: tabla de stack en CLAUDE.md, reglas por capa con ejemplos ✅/❌ y un decision log sembrado con el «por qué» de las decisiones grandes (JPA frente a MyBatis, REST frente a GraphQL, etc.). Leer 5 archivos sale más a cuenta que leer 5.000 fuentes. |
|
|
315
315
|
| **Trabajas en coreano, japonés, chino o 7 idiomas más** | La mayoría de generadores de reglas para Claude Code solo hablan inglés. ClaudeOS-Core escribe el conjunto completo en **10 idiomas** (`en/ko/ja/zh-CN/es/vi/hi/ru/fr/de`) con **validación estructural byte-identical**: el veredicto de `claude-md-validator` no varía según el idioma de salida. |
|
|
@@ -331,7 +331,7 @@ Esto: El código lee tu stack → El código pasa hechos confirmados a Cl
|
|
|
331
331
|
|
|
332
332
|
La pipeline avanza en **tres etapas**, con código tanto antes como después de la llamada al LLM:
|
|
333
333
|
|
|
334
|
-
**1. Etapa A — Scanner (reproducible, sin LLM).** Un scanner en Node.js recorre la raíz del proyecto, lee `package.json`, `build.gradle`, `pom.xml` o `pyproject.toml`, parsea los archivos `.env*` (con redacción automática de variables sensibles como `PASSWORD/SECRET/TOKEN/JWT_SECRET/...`), clasifica el patrón de arquitectura (los 5 patrones A/B/C/D/E de Java, CQRS o multi-módulo en Kotlin, App Router o Pages Router en Next.js, FSD, components-pattern), descubre los dominios y compone una lista explícita con todas las rutas de archivos fuente que existen. El resultado se vuelca en `project-analysis.json`, la única fuente de verdad para lo que viene después.
|
|
334
|
+
**1. Etapa A — Scanner (reproducible, sin LLM).** Un scanner en Node.js recorre la raíz del proyecto, lee `package.json`, `build.gradle`, `build.gradle.kts`, `pom.xml` o `pyproject.toml`, parsea los archivos `.env*` (con redacción automática de variables sensibles como `PASSWORD/SECRET/TOKEN/JWT_SECRET/...`), clasifica el patrón de arquitectura (los 5 patrones A/B/C/D/E de Java, CQRS o multi-módulo en Kotlin, App Router o Pages Router en Next.js, FSD, components-pattern), descubre los dominios y compone una lista explícita con todas las rutas de archivos fuente que existen. El resultado se vuelca en `project-analysis.json`, la única fuente de verdad para lo que viene después.
|
|
335
335
|
|
|
336
336
|
**2. Etapa B — Pipeline de 4 pasos sobre Claude (limitada por los hechos de la etapa A).**
|
|
337
337
|
- **Pass 1** lee archivos representativos por grupo de dominios y extrae unas 50–100 convenciones por dominio: wrappers de respuesta, librería de logging, manejo de errores, convenciones de nombrado, patrones de tests. Se ejecuta una vez por grupo (`max 4 domains, 40 files per group`) para que el contexto nunca se desborde.
|
|
@@ -358,12 +358,14 @@ Para los detalles de cada paso, el sistema de resume basado en marcadores, el tr
|
|
|
358
358
|
|
|
359
359
|
12 stacks que se autodetectan a partir de los archivos del proyecto:
|
|
360
360
|
|
|
361
|
-
**Backend:** Java/Spring Boot · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
361
|
+
**Backend:** Java/Spring Boot (y Spring Framework sin Boot, ver más abajo) · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
362
362
|
|
|
363
363
|
**Frontend:** Node/Next.js · Node/Vite · Angular · Vue/Nuxt
|
|
364
364
|
|
|
365
365
|
Los proyectos multi-stack (por ejemplo, backend Spring Boot con frontend Next.js) funcionan tal cual.
|
|
366
366
|
|
|
367
|
+
**El Java heredado es un objetivo de primera clase (v2.5.1).** Se detecta Spring 1.x–6.x sin Boot en Gradle (era de `apply plugin:`, notación `group:/name:/version:` en cualquier orden, resolución vía `gradle.properties` / `apply from:` / buildSrc, catálogos de versiones, raíces con solo `settings.gradle`), Maven (POMs de Maven 2, propiedades `${spring.version}`, `spring-framework-bom`, raíces multi-módulo, proyectos hermanos sin POM raíz), **Ant + Ivy**, **metadatos de Eclipse / IntelliJ / NetBeans** (`.classpath` incluidas las referencias a JARs no versionados, nivel de cumplimiento de `.settings`, `.idea/misc.xml`, `nbproject`), `WebContent/WEB-INF/lib/**/*.jar`, `WEB-INF/web.xml`, versiones de esquema XSD de Spring y **eGovFrame** — con framework, versión de Spring Framework, nivel de Java, empaquetado `war`/`ear`, driver JDBC y ORM. Los árboles de fuentes con raíz `src/` se escanean con los mismos patrones de dominio que `src/main/java`. Toda versión reportada se lee de un archivo de build, del nombre de un JAR o de una propiedad definida en el proyecto; nada se supone a partir de valores por defecto del framework. Los proyectos JVM sin Spring alguno (`java-library`, `application`, `war` solo de servlets) se reportan como Java con `framework: null`, nunca como Spring.
|
|
368
|
+
|
|
367
369
|
Las reglas de detección y lo que extrae cada scanner están en [docs/es/stacks.md](docs/es/stacks.md).
|
|
368
370
|
|
|
369
371
|
---
|
|
@@ -393,7 +395,7 @@ La mayoría de herramientas de documentación para Claude Code parten de una des
|
|
|
393
395
|
|
|
394
396
|
Esto se traduce en tres consecuencias concretas:
|
|
395
397
|
|
|
396
|
-
1. **Detección reproducible del stack.** Mismo proyecto y mismo código equivalen a la misma
|
|
398
|
+
1. **Detección reproducible del stack y de la estructura.** Mismo proyecto y mismo código equivalen al mismo resultado de escaneo y a la misma disposición de `CLAUDE.md` en 8 secciones. La redacción dentro de cada sección sigue siendo del LLM; lo que está fijado son los hechos que recibe y la forma que debe rellenar.
|
|
397
399
|
2. **Sin rutas inventadas.** El prompt de Pass 3 lleva listadas todas las rutas fuente permitidas; Claude no puede citar nada que no exista.
|
|
398
400
|
3. **Consciente del multi-stack.** En la misma ejecución, los dominios de backend y de frontend usan prompts de análisis distintos.
|
|
399
401
|
|
|
@@ -429,7 +431,7 @@ Más allá de la pipeline de scaffolding, ClaudeOS-Core también prepara la carp
|
|
|
429
431
|
|
|
430
432
|
Son cuatro archivos y los escribe Pass 4:
|
|
431
433
|
|
|
432
|
-
- `decision-log.md`: bitácora append-only de «por qué elegimos X en lugar de Y», sembrada desde `pass2-merged.json
|
|
434
|
+
- `decision-log.md`: bitácora append-only de «por qué elegimos X en lugar de Y», sembrada desde `pass2-merged.json` (nunca se compacta).
|
|
433
435
|
- `failure-patterns.md`: errores recurrentes con puntuaciones de frecuencia e importancia.
|
|
434
436
|
- `compaction.md`: cómo se compacta la memoria automáticamente con el tiempo.
|
|
435
437
|
- `auto-rule-update.md`: patrones que merecen convertirse en reglas nuevas.
|
|
@@ -437,7 +439,7 @@ Son cuatro archivos y los escribe Pass 4:
|
|
|
437
439
|
Dos comandos mantienen viva esta capa:
|
|
438
440
|
|
|
439
441
|
```bash
|
|
440
|
-
# Compactar el log de failure-patterns (ejecuta de vez en cuando)
|
|
442
|
+
# Compactar el log de failure-patterns (ejecuta de vez en cuando; decision-log.md no se toca)
|
|
441
443
|
npx claudeos-core memory compact
|
|
442
444
|
|
|
443
445
|
# Promover los failure patterns frecuentes a propuestas de regla
|
package/README.fr.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
4
4
|
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
5
|
-
[](https://github.com/claudeos-core/claudeos-core/actions/workflows/test.yml)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.npmjs.com/package/claudeos-core)
|
|
@@ -25,7 +25,7 @@ Compatible avec [**12 stacks**](#supported-stacks), monorepos compris. Une seule
|
|
|
25
25
|
|
|
26
26
|
**ClaudeOS-Core régénère ces règles de façon reproductible, à partir du code source réel.** Un scanner Node.js commence par lire le projet : stack, ORM, organisation des paquets, chemins de fichiers. Un pipeline Claude en 4 passes prend ensuite le relais et produit l'ensemble complet : `CLAUDE.md`, les fichiers `.claude/rules/` chargés automatiquement, les standards et les skills. Tout reste cantonné à une liste blanche de chemins explicite, hors de laquelle le LLM ne peut pas sortir. Cinq validateurs contrôlent le résultat avant le rendu final.
|
|
27
27
|
|
|
28
|
-
Conséquence : pour une même entrée, la
|
|
28
|
+
Conséquence : pour une même entrée, on obtient toujours la même structure de `CLAUDE.md` en 8 sections, validée par les mêmes 25 contrôles structurels dans n'importe laquelle des 10 langues, et chaque chemin source cité est vérifié sur le disque. (Le détail se trouve plus bas, dans [Ce qui le rend différent](#ce-qui-le-rend-différent).)
|
|
29
29
|
|
|
30
30
|
Pour les projets qui durent dans le temps, un [Memory Layer](#memory-layer-optionnel-pour-les-projets-longue-durée) distinct est également initialisé.
|
|
31
31
|
|
|
@@ -115,7 +115,7 @@ Exécution sur [`spring-boot-realworld-example-app`](https://github.com/gothinks
|
|
|
115
115
|
</details>
|
|
116
116
|
|
|
117
117
|
<details>
|
|
118
|
-
<summary><strong>Ce qui finit dans votre <code>CLAUDE.md</code> (extrait réel, sections 1 et 2)</strong></summary>
|
|
118
|
+
<summary><strong>Ce qui finit dans votre <code>CLAUDE.md</code> (extrait réel, sections 1 et 2 ; les titres sont abaissés en <code>####</code> pour l'affichage dans le README, le fichier réel utilise <code>## N.</code>)</strong></summary>
|
|
119
119
|
|
|
120
120
|
```markdown
|
|
121
121
|
# CLAUDE.md — spring-boot-realworld-example-app
|
|
@@ -148,7 +148,7 @@ an XML-driven MyBatis persistence layer and JWT-based authentication.
|
|
|
148
148
|
| Test Stack | JUnit Jupiter 5, Mockito, AssertJ, rest-assured, spring-mock-mvc |
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Les lignes du stack (Java 11, Spring Boot 2.6.3, Gradle, MyBatis, SQLite, port 8080) proviennent du scanner déterministe. Les détails plus fins, des coordonnées exactes des dépendances au nom de fichier `dev.db`, en passant par la migration `V1__create_tables.sql` et la mention « no JPA », sont lus par la passe 1 dans `build.gradle`, `application.properties` et l'arborescence source, avec les faits du scanner comme contraintes, puis recoupés par les validateurs. Rien ne vient des valeurs par défaut du framework.
|
|
152
152
|
|
|
153
153
|
</details>
|
|
154
154
|
|
|
@@ -309,7 +309,7 @@ Lorsque `rules/` et `standard/` partagent le même préfixe numérique, ils couv
|
|
|
309
309
|
| Profil | Le problème qui disparaît |
|
|
310
310
|
|---|---|
|
|
311
311
|
| **Développeur solo** qui démarre un nouveau projet avec Claude Code | « Réexpliquer les conventions à Claude à chaque session » : terminé. `CLAUDE.md` et les `.claude/rules/` répartis en 8 catégories sont générés en une seule passe. |
|
|
312
|
-
| **Tech lead** qui maintient des standards communs entre plusieurs dépôts | Les `.claude/rules/` qui dérivent à mesure que les paquets sont renommés, que l'ORM change ou que le wrapper de réponse évolue. ClaudeOS-Core
|
|
312
|
+
| **Tech lead** qui maintient des standards communs entre plusieurs dépôts | Les `.claude/rules/` qui dérivent à mesure que les paquets sont renommés, que l'ORM change ou que le wrapper de réponse évolue. ClaudeOS-Core régénère le tout à partir d'un scaffold fixe en 8 sections : même structure dans chaque dépôt, même verdict du validateur, donc les diffs montrent des changements de convention et non du bruit de mise en page. |
|
|
313
313
|
| **Utilisateur de Claude Code** lassé de corriger le code généré | Mauvais wrapper de réponse, paquets mal organisés, JPA alors que le projet utilise MyBatis, `try/catch` éparpillés alors qu'un middleware centralisé existe. Le scanner extrait les vraies conventions du projet, et chaque passe Claude opère sur une liste blanche de chemins explicite. |
|
|
314
314
|
| **Onboarding sur un nouveau dépôt** (projet existant, intégration en équipe) | Un simple `init` suffit pour obtenir une carte d'architecture vivante : tableau de stack dans CLAUDE.md, rules par couche avec exemples ✅/❌, decision log initialisé avec le « pourquoi » des choix structurants (JPA ou MyBatis, REST ou GraphQL, etc.). Lire 5 fichiers vaut mieux qu'éplucher 5 000 fichiers source. |
|
|
315
315
|
| **Travail en coréen, japonais, chinois ou 7 autres langues** | La plupart des générateurs de rules pour Claude Code se cantonnent à l'anglais. ClaudeOS-Core produit l'ensemble complet dans **10 langues** (`en/ko/ja/zh-CN/es/vi/hi/ru/fr/de`), avec une **validation structurelle identique au bit près**. Le verdict de `claude-md-validator` reste le même quelle que soit la langue de sortie. |
|
|
@@ -331,7 +331,7 @@ Ici : Le code lit votre stack → Le code passe les faits confirmés à Cla
|
|
|
331
331
|
|
|
332
332
|
Le pipeline se déroule en **trois étapes**, avec du code de part et d'autre de l'appel LLM.
|
|
333
333
|
|
|
334
|
-
**1. Étape A — Scanner (reproductible, sans LLM).** Un scanner Node.js parcourt la racine du projet, lit `package.json`, `build.gradle`, `pom.xml` ou `pyproject.toml`, et analyse les fichiers `.env*`. Les variables sensibles (`PASSWORD/SECRET/TOKEN/JWT_SECRET/...`) sont automatiquement masquées. Le scanner classe ensuite le pattern d'architecture (5 patterns Java A/B/C/D/E, Kotlin CQRS ou multi-module, Next.js App ou Pages Router, FSD, components-pattern), identifie les domaines et construit une liste blanche explicite qui recense chaque chemin de fichier source réellement présent. Le tout finit dans `project-analysis.json`, source unique de vérité pour la suite.
|
|
334
|
+
**1. Étape A — Scanner (reproductible, sans LLM).** Un scanner Node.js parcourt la racine du projet, lit `package.json`, `build.gradle`, `build.gradle.kts`, `pom.xml` ou `pyproject.toml`, et analyse les fichiers `.env*`. Les variables sensibles (`PASSWORD/SECRET/TOKEN/JWT_SECRET/...`) sont automatiquement masquées. Le scanner classe ensuite le pattern d'architecture (5 patterns Java A/B/C/D/E, Kotlin CQRS ou multi-module, Next.js App ou Pages Router, FSD, components-pattern), identifie les domaines et construit une liste blanche explicite qui recense chaque chemin de fichier source réellement présent. Le tout finit dans `project-analysis.json`, source unique de vérité pour la suite.
|
|
335
335
|
|
|
336
336
|
**2. Étape B — Pipeline Claude en 4 passes (contraint par les faits de l'étape A).**
|
|
337
337
|
- **Pass 1** lit des fichiers représentatifs par groupe de domaines et en extrait 50 à 100 conventions par domaine : wrappers de réponse, bibliothèques de logging, gestion des erreurs, conventions de naming, patterns de tests. La passe tourne une fois par groupe de domaines (`max 4 domains, 40 files per group`), si bien que le contexte ne déborde jamais.
|
|
@@ -358,12 +358,14 @@ Pour les détails par passe, le mécanisme de reprise par marker, le contourneme
|
|
|
358
358
|
|
|
359
359
|
12 stacks, détectés automatiquement à partir des fichiers de votre projet.
|
|
360
360
|
|
|
361
|
-
**Backend :** Java/Spring Boot · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
361
|
+
**Backend :** Java/Spring Boot (ainsi que Spring Framework sans Boot, voir plus bas) · Kotlin/Spring Boot · Node/Express · Node/Fastify · Node/NestJS · Python/Django · Python/FastAPI · Python/Flask
|
|
362
362
|
|
|
363
363
|
**Frontend :** Node/Next.js · Node/Vite · Angular · Vue/Nuxt
|
|
364
364
|
|
|
365
365
|
Les projets multi-stack (par exemple un backend Spring Boot couplé à un frontend Next.js) fonctionnent sans configuration particulière.
|
|
366
366
|
|
|
367
|
+
**Le Java hérité est une cible de premier plan (v2.5.1).** Spring 1.x–6.x sans Boot est détecté : Gradle (ère `apply plugin:`, notation `group:/name:/version:` dans n'importe quel ordre, résolution via `gradle.properties` / `apply from:` / buildSrc, catalogues de versions, racines ne contenant qu'un `settings.gradle`), Maven (POM Maven 2, propriétés `${spring.version}`, `spring-framework-bom`, racines multi-modules, projets frères sans POM racine), **Ant + Ivy**, **métadonnées Eclipse / IntelliJ / NetBeans** (`.classpath` y compris les références vers des JAR non versionnés, niveau de conformité de `.settings`, `.idea/misc.xml`, `nbproject`), `WebContent/WEB-INF/lib/**/*.jar`, `WEB-INF/web.xml`, versions de schéma XSD Spring et **eGovFrame** — avec le framework, la version de Spring Framework, le niveau Java, le packaging `war`/`ear`, le driver JDBC et l'ORM. Les arborescences dont la racine est `src/` sont scannées avec les mêmes motifs de domaine que `src/main/java`. Chaque version rapportée provient d'un fichier de build, d'un nom de JAR ou d'une propriété définie dans le projet ; rien n'est déduit des valeurs par défaut d'un framework. Les projets JVM sans aucun Spring (`java-library`, `application`, `war` purement servlet) sont rapportés comme Java avec `framework: null`, jamais comme Spring.
|
|
368
|
+
|
|
367
369
|
Pour les règles de détection et le contenu extrait par chaque scanner, voir [docs/fr/stacks.md](docs/fr/stacks.md).
|
|
368
370
|
|
|
369
371
|
---
|
|
@@ -393,7 +395,7 @@ La plupart des outils de documentation pour Claude Code génèrent à partir d'u
|
|
|
393
395
|
|
|
394
396
|
Trois conséquences concrètes en découlent.
|
|
395
397
|
|
|
396
|
-
1. **Détection de stack
|
|
398
|
+
1. **Détection de stack et structure reproductibles.** À projet et code identiques, même résultat de scan et même agencement de `CLAUDE.md` en 8 sections. La formulation à l'intérieur des sections reste écrite par le LLM ; ce qui est figé, ce sont les faits qu'il reçoit et la forme qu'il doit remplir.
|
|
397
399
|
2. **Aucun chemin fabriqué.** Le prompt de la passe 3 énumère noir sur blanc chaque chemin source autorisé : Claude ne peut donc pas citer un chemin qui n'existe pas.
|
|
398
400
|
3. **Pensé multi-stack.** Backend et frontend s'appuient sur des prompts d'analyse distincts, dans une même exécution.
|
|
399
401
|
|
|
@@ -429,7 +431,7 @@ Au-delà du pipeline de scaffolding décrit plus haut, ClaudeOS-Core initialise
|
|
|
429
431
|
|
|
430
432
|
Quatre fichiers, tous écrits par la passe 4 :
|
|
431
433
|
|
|
432
|
-
- `decision-log.md` : journal append-only du « pourquoi avoir choisi X plutôt que Y », initialisé depuis `pass2-merged.json
|
|
434
|
+
- `decision-log.md` : journal append-only du « pourquoi avoir choisi X plutôt que Y », initialisé depuis `pass2-merged.json` (jamais compacté).
|
|
433
435
|
- `failure-patterns.md` : erreurs récurrentes, accompagnées de scores frequency / importance.
|
|
434
436
|
- `compaction.md` : description de la compaction automatique de la mémoire au fil du temps.
|
|
435
437
|
- `auto-rule-update.md` : patterns qui devraient être promus au rang de nouvelles rules.
|
|
@@ -437,7 +439,7 @@ Quatre fichiers, tous écrits par la passe 4 :
|
|
|
437
439
|
Deux commandes assurent la maintenance de cette couche dans la durée :
|
|
438
440
|
|
|
439
441
|
```bash
|
|
440
|
-
# Compacter le log failure-patterns (à lancer périodiquement)
|
|
442
|
+
# Compacter le log failure-patterns (à lancer périodiquement ; decision-log.md n'est pas touché)
|
|
441
443
|
npx claudeos-core memory compact
|
|
442
444
|
|
|
443
445
|
# Promouvoir les failure patterns fréquents en rules proposées
|