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/lib/env-parser.js
CHANGED
|
@@ -220,6 +220,54 @@ function extractApiTarget(vars) {
|
|
|
220
220
|
const SENSITIVE_VAR_PATTERNS = [
|
|
221
221
|
/password/i,
|
|
222
222
|
/passwd/i,
|
|
223
|
+
// Abbreviated form (`DB_PASS`, `MYSQL_PASS`, bare `PASS`). Anchored to a
|
|
224
|
+
// name-segment boundary (start / `_` / `-`) rather than a bare substring,
|
|
225
|
+
// so `BYPASS_AUTH`, `PASSENGER_NAME` and `COMPASS_URL` are NOT swept up.
|
|
226
|
+
// maskUrlCredentials' PARAM_RE has accepted `pass` as a connection
|
|
227
|
+
// parameter name since v2.5.0; this closes the same gap on the env-key
|
|
228
|
+
// side, where the value is copied verbatim into project-analysis.json.
|
|
229
|
+
//
|
|
230
|
+
// KNOWN, ACCEPTED OVER-MATCH: a leading `PASS_` segment also matches
|
|
231
|
+
// benign names such as `PASS_RATE`. Narrowing to a trailing segment
|
|
232
|
+
// (`/(^|[_-])pass$/i`) would fix that but drop `DB_PASS_2` / `PASS_FILE`.
|
|
233
|
+
// The two failure modes are not symmetric — over-redaction costs one
|
|
234
|
+
// config fact in project-analysis.json, under-redaction writes a live
|
|
235
|
+
// credential into a file the Pass 3/4 prompts tell the model to read — so
|
|
236
|
+
// the broader rule stands. Do not narrow it without re-reading this.
|
|
237
|
+
/(^|[_-])pass([_-]|$)/i,
|
|
238
|
+
// Same segment-anchored treatment for the `PW` abbreviation
|
|
239
|
+
// (`DB_PW`, `ADMIN_PW`, `ROOT_PW`).
|
|
240
|
+
/(^|[_-])pw([_-]|$)/i,
|
|
241
|
+
/passphrase/i,
|
|
242
|
+
// Trailing-segment only: `PASSWORD_PEPPER` / `PEPPER` are the secret,
|
|
243
|
+
// `PEPPER_ROUNDS` is a cost parameter.
|
|
244
|
+
/(^|[_-])pepper$/i,
|
|
245
|
+
// Anchored so `SSH_KEYSCAN_HOSTS` (a host list) is not swept up, while
|
|
246
|
+
// `SSH_KEY` / `SSH_KEY_PATH` still are.
|
|
247
|
+
/ssh[_-]?key([_-]|$)/i,
|
|
248
|
+
/sign(ing)?[_-]?key([_-]|$)/i,
|
|
249
|
+
// Specific well-known secret-bearing `*_KEY` names. Deliberately NOT a
|
|
250
|
+
// blanket `/(^|[_-])key([_-]|$)/i`: that would also redact `ROUTING_KEY`,
|
|
251
|
+
// `PARTITION_KEY`, `SORT_KEY`, `IDEMPOTENCY_KEY` and `FOREIGN_KEY_CHECKS`,
|
|
252
|
+
// which are architecture facts this tool exists to document. Each name is
|
|
253
|
+
// anchored at its trailing boundary so `MASTER_KEYSPACE` (Cassandra) and
|
|
254
|
+
// `SERVER_KEYSTORE_PATH` are not swept up.
|
|
255
|
+
//
|
|
256
|
+
// This is a curated list and will always trail real-world naming. If a
|
|
257
|
+
// leak is found, add the specific name here rather than widening to a
|
|
258
|
+
// blanket `key` rule.
|
|
259
|
+
/master[_-]?key([_-]|$)/i,
|
|
260
|
+
/deploy[_-]?key([_-]|$)/i,
|
|
261
|
+
/license[_-]?key([_-]|$)/i,
|
|
262
|
+
/server[_-]?key([_-]|$)/i,
|
|
263
|
+
// Bare `SERVICE_ACCOUNT` (the JSON blob itself) and its secret-bearing
|
|
264
|
+
// suffixes only. `SERVICE_ACCOUNT_EMAIL` / `_NAME` / `_ID` identify the
|
|
265
|
+
// account, they do not authenticate as it — those are config facts.
|
|
266
|
+
// Two patterns, not one with an optional group: an optional group matches
|
|
267
|
+
// empty and the trailing `[_-]` then swallows the separator, which makes
|
|
268
|
+
// `SERVICE_ACCOUNT_EMAIL` match after all.
|
|
269
|
+
/service[_-]?account$/i,
|
|
270
|
+
/service[_-]?account[_-](keys?|json|file|secret|token|creds?|credentials?)([_-]|$)/i,
|
|
223
271
|
/secret/i,
|
|
224
272
|
/api[_-]?key/i,
|
|
225
273
|
/access[_-]?key/i,
|
|
@@ -247,28 +295,65 @@ function isSensitiveVarName(name) {
|
|
|
247
295
|
return SENSITIVE_VAR_PATTERNS.some(re => re.test(name));
|
|
248
296
|
}
|
|
249
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Mask the userinfo component of a URL-shaped value:
|
|
300
|
+
* postgres://app:s3cret@db.internal:5432/app → postgres://***:***@db.internal:5432/app
|
|
301
|
+
* Scheme, host, port, path and query are preserved so consumers can still
|
|
302
|
+
* identify the DB engine / host. Non-URL values pass through unchanged.
|
|
303
|
+
*/
|
|
304
|
+
function maskUrlCredentials(value) {
|
|
305
|
+
if (typeof value !== "string") return value;
|
|
306
|
+
// Scheme may itself contain `:` (`jdbc:postgresql://`, `jdbc:mysql://`).
|
|
307
|
+
// Userinfo is everything between `://` and the LAST `@` of the authority
|
|
308
|
+
// part, so a password containing `@` (`p@ss`) is masked whole. It may NOT
|
|
309
|
+
// contain `/`, `?` or `#`: an `@` that appears after the first `/` belongs
|
|
310
|
+
// to the path or query (`https://cdn.example.com/npm/@scope/pkg`,
|
|
311
|
+
// `/users/@me`, `?redirect=user@host`) and must never be rewritten —
|
|
312
|
+
// masking it would replace the real host with a path fragment. A raw `/`
|
|
313
|
+
// inside a password is not a valid URL and is deliberately left alone.
|
|
314
|
+
// Credentials carried as connection PARAMETERS rather than userinfo:
|
|
315
|
+
// jdbc:postgresql://db/app?user=app&password=s3cret
|
|
316
|
+
// mongodb://host/db?authSource=admin&password=x
|
|
317
|
+
// sqlserver://host;databaseName=app;user=sa;password=x
|
|
318
|
+
// The parameter NAME is kept, the value becomes `***`.
|
|
319
|
+
const PARAM_RE = /([?&;](?:password|passwd|pwd|pass|secret|token|access[_-]?key|secret[_-]?key|api[_-]?key|sas|signature)=)[^&;\s]*/gi;
|
|
320
|
+
if (/^[a-z][a-z0-9+.:-]*:\/\//i.test(value)) {
|
|
321
|
+
return value
|
|
322
|
+
.replace(/^([a-z][a-z0-9+.:-]*:\/\/)([^/?#\s]*)@([^@/?#\s]+)/i, "$1***:***@$3")
|
|
323
|
+
.replace(PARAM_RE, "$1***");
|
|
324
|
+
}
|
|
325
|
+
// Scheme-less credentials are recognized ONLY in the Go/MySQL DSN shape
|
|
326
|
+
// (`user:pw@tcp(host:3306)/db`, `user:pw@unix(/path)/db`); the password may
|
|
327
|
+
// itself contain `@` (`p@ss`) — everything up to the `@` before `tcp(`/`unix(`
|
|
328
|
+
// is userinfo. A generic `a:b@c` rule would corrupt `mailto:ops@example.com`
|
|
329
|
+
// or `0:30@daily`.
|
|
330
|
+
return value
|
|
331
|
+
.replace(/^([^:@/\s]+):(.*)@(?=(?:tcp|unix)\()/, "***:***@")
|
|
332
|
+
.replace(PARAM_RE, "$1***");
|
|
333
|
+
}
|
|
334
|
+
|
|
250
335
|
/**
|
|
251
336
|
* Redacts sensitive values in an env vars map. Returns a new object;
|
|
252
337
|
* original is not mutated. Preserves keys so "variable exists" signal
|
|
253
338
|
* is kept, but replaces values with a sentinel string.
|
|
254
339
|
*
|
|
255
|
-
*
|
|
256
|
-
* db-identification path
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
340
|
+
* v2.5.0 — the former DATABASE_URL whitelist is gone. Its stated
|
|
341
|
+
* justification ("stack-detector's db-identification path depends on it")
|
|
342
|
+
* was stale: stack-detector scans the raw .env text with includes() and
|
|
343
|
+
* never reads envInfo.vars. Meanwhile the unredacted value — typically
|
|
344
|
+
* `postgres://user:password@host/db` — landed verbatim in
|
|
345
|
+
* project-analysis.json, which Pass 3/4 prompts instruct the LLM to read.
|
|
346
|
+
* Every URL-shaped value (DATABASE_URL, REDIS_URL, MONGO_URI, AMQP_URL, …)
|
|
347
|
+
* now has its userinfo masked while keeping scheme/host/path intact.
|
|
261
348
|
*/
|
|
262
349
|
function redactSensitiveVars(vars) {
|
|
263
350
|
if (!vars || typeof vars !== "object") return vars;
|
|
264
351
|
const out = {};
|
|
265
352
|
for (const [k, v] of Object.entries(vars)) {
|
|
266
|
-
if (k
|
|
267
|
-
out[k] = v; // documented whitelist for stack-detector back-compat
|
|
268
|
-
} else if (isSensitiveVarName(k)) {
|
|
353
|
+
if (isSensitiveVarName(k)) {
|
|
269
354
|
out[k] = "***REDACTED***";
|
|
270
355
|
} else {
|
|
271
|
-
out[k] = v;
|
|
356
|
+
out[k] = maskUrlCredentials(v);
|
|
272
357
|
}
|
|
273
358
|
}
|
|
274
359
|
return out;
|
|
@@ -293,8 +378,8 @@ function readStackEnvInfo(root) {
|
|
|
293
378
|
source: file,
|
|
294
379
|
vars: redactSensitiveVars(vars),
|
|
295
380
|
port: extractPort(vars),
|
|
296
|
-
host: extractHost(vars),
|
|
297
|
-
apiTarget: extractApiTarget(vars),
|
|
381
|
+
host: maskUrlCredentials(extractHost(vars)),
|
|
382
|
+
apiTarget: maskUrlCredentials(extractApiTarget(vars)),
|
|
298
383
|
};
|
|
299
384
|
}
|
|
300
385
|
|
|
@@ -308,6 +393,7 @@ module.exports = {
|
|
|
308
393
|
readStackEnvInfo,
|
|
309
394
|
isSensitiveVarName,
|
|
310
395
|
redactSensitiveVars,
|
|
396
|
+
maskUrlCredentials,
|
|
311
397
|
// Exported for test visibility:
|
|
312
398
|
ENV_FILE_ORDER,
|
|
313
399
|
PORT_VAR_KEYS,
|
package/lib/memory-scaffold.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const path = require("path");
|
|
12
12
|
const fs = require("fs");
|
|
13
|
+
const crypto = require("crypto");
|
|
13
14
|
const { ensureDir, existsSafe, writeFileSafe, readFileSafe } = require("./safe-fs");
|
|
14
15
|
|
|
15
16
|
// ─── Language labels — single source of truth: lib/language-config.js ──
|
|
@@ -58,6 +59,18 @@ function writeCache(cacheFile, cache) {
|
|
|
58
59
|
} catch (_e) { /* best-effort */ }
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
// v2.5.0 — Cache entries are keyed by content name AND a hash of the English
|
|
63
|
+
// source. Pre-v2.5.0 the key was the bare name (`MEMORY_FILES.compaction.md`),
|
|
64
|
+
// so whenever a static-fallback text changed between releases, a project that
|
|
65
|
+
// already had `fallback-cache-<lang>.json` kept serving the translation of the
|
|
66
|
+
// OLD English text forever. Hashing the source makes a text change a cache
|
|
67
|
+
// miss automatically; stale entries under the old key shape are simply never
|
|
68
|
+
// read again.
|
|
69
|
+
function cacheKeyFor(contentKey, englishContent) {
|
|
70
|
+
const h = crypto.createHash("sha1").update(String(englishContent), "utf8").digest("hex").slice(0, 12);
|
|
71
|
+
return `${contentKey}@${h}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
61
74
|
// Translate English static content to the requested language via Claude CLI.
|
|
62
75
|
//
|
|
63
76
|
// Behavior contract:
|
|
@@ -81,6 +94,15 @@ function translateIfNeeded(englishContent, lang, contentKey, cacheFile) {
|
|
|
81
94
|
);
|
|
82
95
|
}
|
|
83
96
|
|
|
97
|
+
// Cache hit → return immediately. Checked BEFORE the env-skip below: a hit
|
|
98
|
+
// never shells out, so the skip has nothing to prevent, and this keeps the
|
|
99
|
+
// cache path unit-testable under CLAUDEOS_SKIP_TRANSLATION=1.
|
|
100
|
+
const cache = readCache(cacheFile);
|
|
101
|
+
const key = cacheKeyFor(contentKey, englishContent);
|
|
102
|
+
if (cache[key] && typeof cache[key] === "string" && cache[key].trim().length > 0) {
|
|
103
|
+
return cache[key];
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
// M2: CI / deterministic-test escape hatch. When
|
|
85
107
|
// CLAUDEOS_SKIP_TRANSLATION=1 is set, throw instead of shelling out to
|
|
86
108
|
// `claude -p`. This makes the "translation fails without real Claude CLI"
|
|
@@ -95,12 +117,6 @@ function translateIfNeeded(englishContent, lang, contentKey, cacheFile) {
|
|
|
95
117
|
);
|
|
96
118
|
}
|
|
97
119
|
|
|
98
|
-
// Cache hit → return immediately
|
|
99
|
-
const cache = readCache(cacheFile);
|
|
100
|
-
if (cache[contentKey] && typeof cache[contentKey] === "string" && cache[contentKey].trim().length > 0) {
|
|
101
|
-
return cache[contentKey];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
120
|
// Lazy-load CLI utils to avoid a circular dependency at module-load time.
|
|
105
121
|
let runClaudeCapture;
|
|
106
122
|
try {
|
|
@@ -256,8 +272,9 @@ Translate now. Output only the translated document.`;
|
|
|
256
272
|
);
|
|
257
273
|
}
|
|
258
274
|
|
|
259
|
-
// Success — save to cache for future init runs
|
|
260
|
-
|
|
275
|
+
// Success — save to cache for future init runs (content-hashed key; must
|
|
276
|
+
// be recomputed here — this function does not share translateIfNeeded's scope).
|
|
277
|
+
cache[cacheKeyFor(contentKey, englishContent)] = cleaned;
|
|
261
278
|
writeCache(cacheFile, cache);
|
|
262
279
|
|
|
263
280
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
@@ -282,7 +299,7 @@ _Format: \`## <pattern-id>\` with Frequency / Last Seen / Importance / Fix._
|
|
|
282
299
|
|
|
283
300
|
"compaction.md": `# Compaction Strategy
|
|
284
301
|
|
|
285
|
-
_4-stage compaction rules for \`decision-log.md\` and
|
|
302
|
+
_4-stage compaction rules for \`failure-patterns.md\`. \`decision-log.md\` is append-only and never compacted._
|
|
286
303
|
_Run via \`npx claudeos-core memory compact\`._
|
|
287
304
|
|
|
288
305
|
## Preservation Priority
|
|
@@ -431,8 +448,8 @@ Before analysis or evaluation, review this table and do not repeat these pattern
|
|
|
431
448
|
| 8 | Making quantitative judgments based on unverified numbers, then reversing when challenged | No speculation-based quantitative claims |
|
|
432
449
|
| 9 | Judging a standard document as "low practical value" without reading it | No judgment before verification |
|
|
433
450
|
| 10 | Proposing to merge/reduce same-layer intentional duplication | Different perspectives on the same topic in different files is intentional. Rule accessibility > token saving |
|
|
434
|
-
| 11 | Suggesting an import or package name without verifying it exists in the dependency manifest | Hallucinated module/named export. Verify by reading \`package.json\` / \`pom.xml\` / \`build.gradle\` / \`pyproject.toml\` / \`requirements.txt\` before recommending an import |
|
|
435
|
-
| 12 | Mixing API signatures from different major versions of the same library | Verify by checking the manifest AND lockfile for the exact installed version (lockfile pins the truth): \`package-lock.json\`/\`pnpm-lock.yaml\`/\`yarn.lock\`, \`gradle.lockfile\`/\`gradle/libs.versions.toml\`, \`poetry.lock\`/\`Pipfile.lock\`/\`uv.lock\`, or fall back to the manifest (\`pom.xml\`/\`build.gradle\`/\`package.json\`/\`pyproject.toml\`) |
|
|
451
|
+
| 11 | Suggesting an import or package name without verifying it exists in the dependency manifest | Hallucinated module/named export. Verify by reading \`package.json\` / \`pom.xml\` / \`build.gradle\` / \`build.gradle.kts\` / \`pyproject.toml\` / \`requirements.txt\` before recommending an import |
|
|
452
|
+
| 12 | Mixing API signatures from different major versions of the same library | Verify by checking the manifest AND lockfile for the exact installed version (lockfile pins the truth): \`package-lock.json\`/\`pnpm-lock.yaml\`/\`yarn.lock\`, \`gradle.lockfile\`/\`gradle/libs.versions.toml\`, \`poetry.lock\`/\`Pipfile.lock\`/\`uv.lock\`, or fall back to the manifest (\`pom.xml\`/\`build.gradle\`/\`build.gradle.kts\`/\`package.json\`/\`pyproject.toml\`) |
|
|
436
453
|
| 13 | Editing one environment config file without checking sibling parity | Verify by \`Glob\` for the config family before a partial edit. Backend: \`.env*\`, \`application-*.yml/.properties\`, \`*settings.py\`. Frontend: \`environment*.ts\` (Angular), \`next.config.*\`, \`vite.config.*\`, \`nuxt.config.*\`, \`.env.local\`/\`.env.production\` |
|
|
437
454
|
| 14 | Mixing server/client component boundaries (SSR frameworks) — using client-only APIs (\`useState\`, \`useEffect\`, \`window\`, \`localStorage\`) in a server component, or server-only operations (DB access, secret reads, filesystem) in a client component | Read the file's boundary directive before adding code: Next.js App Router (\`"use client"\` opt-in), Nuxt (server vs client composables), Remix (\`loader\`/\`action\` exports). N/A for pure SPA or pure backend projects |
|
|
438
455
|
| 15 | Inventing component prop names or function arguments without reading the target's interface | Read the target's prop type definition (\`interface Props\`, \`defineProps<>\`, function signature) or method signature before invoking. Hallucinated props may compile in loose-TS code and crash at runtime |
|
|
@@ -450,9 +467,8 @@ Before analysis or evaluation, review this table and do not repeat these pattern
|
|
|
450
467
|
- **Do not modify onboarding/guide documents (\`claudeos-core/guide/\`) unless specifically requested.**
|
|
451
468
|
- Do not clean up surrounding code during bug fixes, or add unnecessary refactoring during feature additions.
|
|
452
469
|
- **Empty directories may be intentional placeholders — verify markers before flagging or removing.** Markers of intent: a \`.gitkeep\` / \`KEEP_EMPTY.md\` file inside, the directory listed in CLAUDE.md as planned, or referenced by an active plan/standard/skills document. If none of these exist, an empty directory may be neglect — ask the user before deleting.
|
|
453
|
-
- **\`plan/\` master documents are internal sync management tools.** Do not suggest removing them. "DO NOT Read" means "AI should not read directly", not "the file is unnecessary".
|
|
454
470
|
- **Do not duplicate into memory what is already directly verifiable in code, config files, or rule documents.** Configuration values, paths, and names already recorded in CLAUDE.md, rules, or standard must not be redundantly stored in memory. Examples — backend: port numbers, pool sizes, handler names, transaction propagation modes; frontend: dev server port, build output dir, env var prefix (\`VITE_\`/\`NEXT_PUBLIC_\`/\`REACT_APP_\`), route definitions, bundle size budgets.
|
|
455
|
-
- **Do not directly read internal document directories (\`guide/\`, \`
|
|
471
|
+
- **Do not directly read internal document directories (\`guide/\`, \`generated/\`, \`mcp-guide/\`) for routine context loading.** \`.claude/rules/\` and \`claudeos-core/standard/\` already contain the essential content. *Exception: read directly when the user explicitly asks about these contents, or when debugging an issue requires inspecting them.*
|
|
456
472
|
- **Established codebase conventions take precedence over textbook-ideal patterns.** Propose modernization, refactoring, or "current best practices" migration ONLY when the user explicitly requests it (e.g., "modernize", "migrate to v3", "refactor to current best practices"). Otherwise, follow the existing pattern even if you would write it differently in a greenfield project.
|
|
457
473
|
|
|
458
474
|
## Project Architecture — Hands Off
|
|
@@ -464,7 +480,7 @@ This project uses the CLAUDE.md → rules → standard 3-layer architecture, plu
|
|
|
464
480
|
- **Memory (\`claudeos-core/memory/\`, on-demand) and Rules (\`.claude/rules/\`, auto-loaded by path match) are never simultaneously loaded by default.** Similar content between them is NOT duplication. Do not propose "memory cleanup", "dedupe memory vs rules", or "consolidate memory into rules" — they have different roles (on-demand history/context vs auto-loaded enforcement).
|
|
465
481
|
- **Multi-rule load provides reinforcement.** Do not label co-loaded defensive rules as "redundant" — the reinforcement effect is intentional and the context cost is negligible.
|
|
466
482
|
- **Rules \`paths: ["**/*"]\` — do not propose conditional path conversion for core rules.** When developing new features (no matching files exist yet), conditional paths would prevent rules from loading, risking non-standard code generation.
|
|
467
|
-
- **\`00.standard-reference.md\`
|
|
483
|
+
- **\`00.standard-reference.md\` is a paths-only index of \`claudeos-core/standard/\`.** When a standard file is added, add its path there (one line, no description). Do not add rule files, skill files, memory files, or "DO NOT Read" lists to it — those belong elsewhere, and every extra line is reloaded on every edit.
|
|
468
484
|
- **Minor wording or item count differences across layers are NOT "inconsistency risks".** If no information is missing, trivial expression differences are not a problem.
|
|
469
485
|
|
|
470
486
|
## Planned References — No "Missing" Judgment
|
|
@@ -577,8 +593,8 @@ paths:
|
|
|
577
593
|
|
|
578
594
|
# Compaction Strategy (\`memory/compaction.md\`)
|
|
579
595
|
|
|
580
|
-
Reference document defining the 4-stage compaction policy.
|
|
581
|
-
Executed by \`npx claudeos-core memory compact\`.
|
|
596
|
+
Reference document defining the 4-stage compaction policy for \`failure-patterns.md\`.
|
|
597
|
+
Executed by \`npx claudeos-core memory compact\`. \`decision-log.md\` is never compacted (append-only).
|
|
582
598
|
|
|
583
599
|
## Rules
|
|
584
600
|
|
|
@@ -1055,4 +1071,7 @@ module.exports = {
|
|
|
1055
1071
|
scaffoldMasterPlans,
|
|
1056
1072
|
scaffoldDocWritingGuide,
|
|
1057
1073
|
scaffoldSkillsManifest,
|
|
1074
|
+
// Exported for test visibility (v2.5.0: content-hashed translation cache).
|
|
1075
|
+
translateIfNeeded,
|
|
1076
|
+
_cacheKeyFor: cacheKeyFor,
|
|
1058
1077
|
};
|
|
@@ -166,10 +166,21 @@ async function main() {
|
|
|
166
166
|
// Deterministically reconcile MANIFEST.md ↔ CLAUDE.md §6 cross-references
|
|
167
167
|
// that LLM stages routinely drift on. Failure here is logged but not
|
|
168
168
|
// fatal — manifest-generator's primary outputs above are already on disk.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
//
|
|
170
|
+
// OPT-IN ONLY. This step WRITES to CLAUDE.md and MANIFEST.md. It must not
|
|
171
|
+
// run from `npx claudeos-core health` (documented as a read-only gate that
|
|
172
|
+
// users wire into CI / pre-commit) — a health check that dirties the
|
|
173
|
+
// working tree is a trap. `init` passes `--sync-skills` after Pass 3/4;
|
|
174
|
+
// everything else gets a pure metadata generation run. The flag is the ONLY
|
|
175
|
+
// switch — an environment variable would be inherited by `health`'s child
|
|
176
|
+
// process and silently re-enable writes from a shell that exported it.
|
|
177
|
+
const syncRequested = process.argv.includes("--sync-skills");
|
|
178
|
+
if (syncRequested) {
|
|
179
|
+
try {
|
|
180
|
+
syncSkillsCatalog(ROOT);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
console.log(` ⚠️ skills-sync: unexpected error (${e.message || e})`);
|
|
183
|
+
}
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
// ─── Initialize stale-report.json (preserve existing sub-tool results) ──
|
package/package.json
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claudeos-core",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Auto-generate Claude Code documentation from your actual source code — Standards, Rules, Skills, and Guides tailored to your project",
|
|
5
|
-
"main": "bin/cli.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"claudeos-core": "bin/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin/",
|
|
11
|
-
"lib/",
|
|
12
|
-
"claude-md-validator/",
|
|
13
|
-
"content-validator/",
|
|
14
|
-
"health-checker/",
|
|
15
|
-
"manifest-generator/",
|
|
16
|
-
"pass-json-validator/",
|
|
17
|
-
"pass-prompts/",
|
|
18
|
-
"plan-installer/",
|
|
19
|
-
"plan-validator/",
|
|
20
|
-
"sync-checker/",
|
|
21
|
-
"bootstrap.sh",
|
|
22
|
-
"README.md",
|
|
23
|
-
"README.ko.md",
|
|
24
|
-
"LICENSE",
|
|
25
|
-
"CHANGELOG.md",
|
|
26
|
-
"CONTRIBUTING.md",
|
|
27
|
-
"CODE_OF_CONDUCT.md",
|
|
28
|
-
"SECURITY.md",
|
|
29
|
-
"README.zh-CN.md",
|
|
30
|
-
"README.ja.md",
|
|
31
|
-
"README.es.md",
|
|
32
|
-
"README.vi.md",
|
|
33
|
-
"README.hi.md",
|
|
34
|
-
"README.ru.md",
|
|
35
|
-
"README.fr.md",
|
|
36
|
-
"README.de.md"
|
|
37
|
-
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"init": "node bin/cli.js init",
|
|
40
|
-
"lint": "node bin/cli.js lint",
|
|
41
|
-
"health": "node bin/cli.js health",
|
|
42
|
-
"validate": "node bin/cli.js validate",
|
|
43
|
-
"refresh": "node bin/cli.js refresh",
|
|
44
|
-
"restore": "node bin/cli.js restore",
|
|
45
|
-
"pretest": "node -e \"try{require('glob')}catch(e){process.exit(1)}\" || npm install",
|
|
46
|
-
"test": "node scripts/run-tests.js",
|
|
47
|
-
"test:health": "node health-checker/index.js"
|
|
48
|
-
},
|
|
49
|
-
"keywords": [
|
|
50
|
-
"claude-code",
|
|
51
|
-
"automation",
|
|
52
|
-
"code-analysis",
|
|
53
|
-
"CLAUDE.md",
|
|
54
|
-
"standards",
|
|
55
|
-
"rules",
|
|
56
|
-
"skills",
|
|
57
|
-
"scaffolding",
|
|
58
|
-
"i18n",
|
|
59
|
-
"multi-language",
|
|
60
|
-
"spring-boot",
|
|
61
|
-
"kotlin",
|
|
62
|
-
"exposed",
|
|
63
|
-
"jooq",
|
|
64
|
-
"cqrs",
|
|
65
|
-
"bff",
|
|
66
|
-
"multi-module",
|
|
67
|
-
"monorepo",
|
|
68
|
-
"nextjs",
|
|
69
|
-
"express",
|
|
70
|
-
"fastify",
|
|
71
|
-
"angular",
|
|
72
|
-
"django",
|
|
73
|
-
"fastapi"
|
|
74
|
-
],
|
|
75
|
-
"author": "claudeos-core <claudeoscore@gmail.com> (https://github.com/claudeos-core)",
|
|
76
|
-
"license": "ISC",
|
|
77
|
-
"repository": {
|
|
78
|
-
"type": "git",
|
|
79
|
-
"url": "git+https://github.com/claudeos-core/claudeos-core.git"
|
|
80
|
-
},
|
|
81
|
-
"homepage": "https://github.com/claudeos-core/claudeos-core#readme",
|
|
82
|
-
"bugs": {
|
|
83
|
-
"url": "https://github.com/claudeos-core/claudeos-core/issues"
|
|
84
|
-
},
|
|
85
|
-
"engines": {
|
|
86
|
-
"node": ">=18.0.0"
|
|
87
|
-
},
|
|
88
|
-
"dependencies": {
|
|
89
|
-
"glob": "^13.0.6",
|
|
90
|
-
"gray-matter": "^4.0.3"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claudeos-core",
|
|
3
|
+
"version": "2.5.1",
|
|
4
|
+
"description": "Auto-generate Claude Code documentation from your actual source code — Standards, Rules, Skills, and Guides tailored to your project",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claudeos-core": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"claude-md-validator/",
|
|
13
|
+
"content-validator/",
|
|
14
|
+
"health-checker/",
|
|
15
|
+
"manifest-generator/",
|
|
16
|
+
"pass-json-validator/",
|
|
17
|
+
"pass-prompts/",
|
|
18
|
+
"plan-installer/",
|
|
19
|
+
"plan-validator/",
|
|
20
|
+
"sync-checker/",
|
|
21
|
+
"bootstrap.sh",
|
|
22
|
+
"README.md",
|
|
23
|
+
"README.ko.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"CONTRIBUTING.md",
|
|
27
|
+
"CODE_OF_CONDUCT.md",
|
|
28
|
+
"SECURITY.md",
|
|
29
|
+
"README.zh-CN.md",
|
|
30
|
+
"README.ja.md",
|
|
31
|
+
"README.es.md",
|
|
32
|
+
"README.vi.md",
|
|
33
|
+
"README.hi.md",
|
|
34
|
+
"README.ru.md",
|
|
35
|
+
"README.fr.md",
|
|
36
|
+
"README.de.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"init": "node bin/cli.js init",
|
|
40
|
+
"lint": "node bin/cli.js lint",
|
|
41
|
+
"health": "node bin/cli.js health",
|
|
42
|
+
"validate": "node bin/cli.js validate",
|
|
43
|
+
"refresh": "node bin/cli.js refresh",
|
|
44
|
+
"restore": "node bin/cli.js restore",
|
|
45
|
+
"pretest": "node -e \"try{require('glob')}catch(e){process.exit(1)}\" || npm install",
|
|
46
|
+
"test": "node scripts/run-tests.js",
|
|
47
|
+
"test:health": "node health-checker/index.js"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"claude-code",
|
|
51
|
+
"automation",
|
|
52
|
+
"code-analysis",
|
|
53
|
+
"CLAUDE.md",
|
|
54
|
+
"standards",
|
|
55
|
+
"rules",
|
|
56
|
+
"skills",
|
|
57
|
+
"scaffolding",
|
|
58
|
+
"i18n",
|
|
59
|
+
"multi-language",
|
|
60
|
+
"spring-boot",
|
|
61
|
+
"kotlin",
|
|
62
|
+
"exposed",
|
|
63
|
+
"jooq",
|
|
64
|
+
"cqrs",
|
|
65
|
+
"bff",
|
|
66
|
+
"multi-module",
|
|
67
|
+
"monorepo",
|
|
68
|
+
"nextjs",
|
|
69
|
+
"express",
|
|
70
|
+
"fastify",
|
|
71
|
+
"angular",
|
|
72
|
+
"django",
|
|
73
|
+
"fastapi"
|
|
74
|
+
],
|
|
75
|
+
"author": "claudeos-core <claudeoscore@gmail.com> (https://github.com/claudeos-core)",
|
|
76
|
+
"license": "ISC",
|
|
77
|
+
"repository": {
|
|
78
|
+
"type": "git",
|
|
79
|
+
"url": "git+https://github.com/claudeos-core/claudeos-core.git"
|
|
80
|
+
},
|
|
81
|
+
"homepage": "https://github.com/claudeos-core/claudeos-core#readme",
|
|
82
|
+
"bugs": {
|
|
83
|
+
"url": "https://github.com/claudeos-core/claudeos-core/issues"
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=18.0.0"
|
|
87
|
+
},
|
|
88
|
+
"dependencies": {
|
|
89
|
+
"glob": "^13.0.6",
|
|
90
|
+
"gray-matter": "^4.0.3"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -228,7 +228,7 @@ async function main() {
|
|
|
228
228
|
const framework = paData.stack?.framework;
|
|
229
229
|
const language = paData.stack?.language;
|
|
230
230
|
const architecture = paData.stack?.architecture;
|
|
231
|
-
isBackend = !frontend || ["express", "nestjs", "fastify", "django", "fastapi", "flask", "spring-boot"].includes(framework);
|
|
231
|
+
isBackend = !frontend || ["express", "nestjs", "fastify", "django", "fastapi", "flask", "spring-boot", "spring-framework"].includes(framework);
|
|
232
232
|
isKotlin = language === "kotlin";
|
|
233
233
|
isKotlinCqrs = isKotlin && (architecture === "cqrs" || paData.stack?.multiModule);
|
|
234
234
|
} catch (_e) { /* If project-analysis parsing fails, conservatively assume backend */ }
|
|
@@ -85,6 +85,7 @@ Generation targets:
|
|
|
85
85
|
- `60.memory/*` rules: forward reference — Pass 4 will generate 4 files (01.decision-log, 02.failure-patterns, 03.compaction, 04.auto-rule-update), each with file-specific `paths`. Pass 3 must STILL list ```.claude/rules/60.memory/*``` as a row in CLAUDE.md Section 6 Rules table so developers/Claude see the category exists.
|
|
86
86
|
- `70.domains/*` rules (multi-domain projects only): per-domain rules at `.claude/rules/70.domains/{type}/{domain}-rules.md` (where `{type}` is `backend` or `frontend`, ALWAYS present even in single-stack projects for uniform layout + zero-migration future-proofing), each with a `paths:` glob scoped to that domain's source directories so the rule auto-loads only when editing files within the relevant domain. Folder name is PLURAL (`domains/`) — collection of N per-domain files — and each file inside uses the SINGULAR domain name (`{domain}-rules.md`). DO NOT use `60.domains/` (collides with `60.memory/`) and DO NOT skip the `{type}/` sub-folder. See pass3-footer.md "Per-domain folder convention" for the full rationale.
|
|
87
87
|
- MUST generate `.claude/rules/00.core/00.standard-reference.md` as a directory of all standard files
|
|
88
|
+
(paths only, grouped by category). Include the FORWARD REFERENCE `claudeos-core/standard/00.core/04.doc-writing-guide.md` under `## Core` — Pass 4 generates it; omitting it leaves an index gap the moment Pass 4 runs. Do NOT add a "DO NOT Read" section here — CLAUDE.md Section 7 is the single source of truth.
|
|
88
89
|
|
|
89
90
|
4. .claude/rules/50.sync/ (2 sync rules)
|
|
90
91
|
- 01.doc-sync.md — Bidirectional standard ↔ rules sync reminder (both directions in ONE rule).
|
|
@@ -94,7 +95,7 @@ Generation targets:
|
|
|
94
95
|
- 02.skills-sync.md — Remind AI to update MANIFEST.md when skills are modified
|
|
95
96
|
|
|
96
97
|
5. claudeos-core/skills/ (active domains only)
|
|
97
|
-
- 20.frontend-page/01.scaffold-feature
|
|
98
|
+
- 20.frontend-page/01.scaffold-page-feature.md (orchestrator — Angular feature module scaffolding; stem MUST match the `scaffold-page-feature/` sub-folder so content-validator pairs them)
|
|
98
99
|
- 20.frontend-page/scaffold-page-feature/01~08 (sub-skills: module, component, service, routing, template, test, style, index)
|
|
99
100
|
- 00.shared/MANIFEST.md (skill registry)
|
|
100
101
|
|
|
@@ -277,7 +277,7 @@ Unlike rules that auto-load via `paths` glob, this layer is referenced **on-dema
|
|
|
277
277
|
2. **Skim recent decisions**: Skim recent entries in `decision-log.md` to avoid overwriting architectural decisions that have already been agreed upon.
|
|
278
278
|
3. **Record new decisions**: When making a significant design decision (choosing between competing patterns, adopting or rejecting a library, fixing a convention, etc.), append to `decision-log.md`.
|
|
279
279
|
4. **Record repeated errors**: If the same error occurs ≥2 times and the root cause is non-obvious, register it in `failure-patterns.md` with a new pattern-id.
|
|
280
|
-
5. **Periodic compaction**: When
|
|
280
|
+
5. **Periodic compaction**: When `failure-patterns.md` approaches 400 lines or has not been tidied up for over a month, run `npx claudeos-core memory compact` (`decision-log.md` is append-only and is never compacted).
|
|
281
281
|
6. **Review rule-update proposals**: Review proposals in `auto-rule-update.md` with confidence ≥ 0.70. When accepting, edit the corresponding rule file and log the decision in `decision-log.md`.
|
|
282
282
|
|
|
283
283
|
**Session Resume (after auto-compact or restart)**: Claude Code's auto-compact feature may truncate session context mid-work, and a restarted session starts with a fresh context window. When resuming work:
|
|
@@ -125,11 +125,15 @@ IMPORT, not redefine.
|
|
|
125
125
|
- ...
|
|
126
126
|
- ...
|
|
127
127
|
|
|
128
|
-
## Allowed Source Paths (v2.
|
|
128
|
+
## Allowed Source Paths (v2.5.0+ — WRITTEN BY THE ORCHESTRATOR)
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
**Do NOT write this section yourself.** After you finish, the Node.js
|
|
131
|
+
orchestrator appends `## Allowed Source Paths` to this file directly from
|
|
132
|
+
`project-analysis.json` — byte-exact, every run. Anything you write under
|
|
133
|
+
that heading will be replaced. Skip it and save the tokens.
|
|
134
|
+
|
|
135
|
+
The remainder of this section documents the shape the orchestrator emits,
|
|
136
|
+
so Pass 3b/3c/3d know what to expect:
|
|
133
137
|
|
|
134
138
|
- Header: whether the list is in `full` mode (individual file paths) or
|
|
135
139
|
`rollup` mode (parent directories, used when the project exceeds the
|
|
@@ -175,11 +179,9 @@ fall back to pass2-merged.json verification per file)` instead.
|
|
|
175
179
|
3. **Exact values only.** Every class name, method name, package path, and
|
|
176
180
|
file path must be verbatim from the analysis data. If a value is not
|
|
177
181
|
captured in the analysis, write `(not in analysis)` — do NOT guess.
|
|
178
|
-
4. **Allowed Source Paths section
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
path you drop here becomes a path they can fabricate later without
|
|
182
|
-
the downstream validator catching it until after Pass 3 completes.
|
|
182
|
+
4. **Do not write the `## Allowed Source Paths` section.** The orchestrator
|
|
183
|
+
injects it verbatim from `project-analysis.json` once this step ends
|
|
184
|
+
(v2.5.0+). It is deterministic by design — no LLM copying involved.
|
|
183
185
|
5. **Do NOT write any other files.** CLAUDE.md, standard/, rules/, etc.
|
|
184
186
|
come in later Pass 3 steps. Writing them here is a bug.
|
|
185
187
|
6. **Do NOT read source code.** All information comes from the three JSON
|
|
@@ -191,7 +191,7 @@ Baseline template (translate per above, then append the project-specific section
|
|
|
191
191
|
```markdown
|
|
192
192
|
# Compaction Strategy
|
|
193
193
|
|
|
194
|
-
_4-stage compaction rules for `decision-log.md` and
|
|
194
|
+
_4-stage compaction rules for `failure-patterns.md`. `decision-log.md` is append-only and never compacted._
|
|
195
195
|
_Run via `npx claudeos-core memory compact`._
|
|
196
196
|
|
|
197
197
|
## Preservation Priority
|
|
@@ -266,7 +266,7 @@ Body (write in **{{LANG_NAME}}**) must cover:
|
|
|
266
266
|
Frontmatter: `name: AI Work Rules`, `paths: ["**/*"]`
|
|
267
267
|
Body (write in **{{LANG_NAME}}**) must cover:
|
|
268
268
|
- Accuracy over token saving; verify before claiming
|
|
269
|
-
-
|
|
269
|
+
- 17 hallucination prevention patterns (see memory-scaffold static fallback `RULE_FILES_00["52.ai-work-rules.md"]` for the reference table)
|
|
270
270
|
- No unsolicited suggestions; ask when unsure
|
|
271
271
|
- Memory vs Rules — no duplication judgment (Memory is on-demand, Rules are auto-loaded)
|
|
272
272
|
- Planned references — no "missing" judgment
|
|
@@ -374,7 +374,7 @@ After all files are written, create:
|
|
|
374
374
|
"standardFiles": [
|
|
375
375
|
"claudeos-core/standard/00.core/XX.doc-writing-guide.md"
|
|
376
376
|
],
|
|
377
|
-
"claudeMdAppended":
|
|
377
|
+
"claudeMdAppended": false,
|
|
378
378
|
"seededDecisions": <integer count of seed entries written to decision-log.md>
|
|
379
379
|
}
|
|
380
380
|
```
|
|
@@ -21,8 +21,16 @@ Required reads (if the file exists):
|
|
|
21
21
|
the variable inside `ext { ... }` or `<properties>`. Record the
|
|
22
22
|
ACTUAL Java version — do NOT infer "Java 17+" from the Spring
|
|
23
23
|
Boot version.
|
|
24
|
-
- Spring
|
|
25
|
-
frameworkVersion field
|
|
24
|
+
- Spring version: verify it matches `project-analysis.json`'s
|
|
25
|
+
frameworkVersion field (Spring Boot, or the Spring Framework line when
|
|
26
|
+
`framework` is `"spring-framework"` — a pre-Boot project); if they
|
|
27
|
+
disagree, trust the build file. For a non-Boot project, also read
|
|
28
|
+
`WEB-INF/web.xml` and the Spring XML configs it references
|
|
29
|
+
(`applicationContext*.xml`, `*-servlet.xml`, `*.properties`) — they
|
|
30
|
+
hold what `application.yml` holds in a Boot project, and step 2 below
|
|
31
|
+
will find no `application.yml`. Do NOT describe Boot features
|
|
32
|
+
(auto-configuration, starters, actuator, `@SpringBootApplication`)
|
|
33
|
+
for a project that does not declare Boot.
|
|
26
34
|
- Dependencies that indicate specific patterns (MyBatis/iBatis/JPA,
|
|
27
35
|
multiple DB drivers, Jasypt, JWT library, Logback extras).
|
|
28
36
|
|