claudeos-core 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1460 -73
- package/CODE_OF_CONDUCT.md +15 -0
- package/README.de.md +321 -883
- package/README.es.md +322 -883
- package/README.fr.md +322 -883
- package/README.hi.md +322 -883
- package/README.ja.md +322 -883
- package/README.ko.md +322 -882
- package/README.md +321 -883
- package/README.ru.md +322 -885
- package/README.vi.md +322 -883
- package/README.zh-CN.md +321 -881
- package/SECURITY.md +51 -0
- package/bin/commands/init.js +570 -264
- package/content-validator/index.js +185 -12
- package/health-checker/index.js +44 -10
- package/package.json +92 -90
- package/pass-json-validator/index.js +58 -7
- package/pass-prompts/templates/angular/pass3.md +15 -14
- package/pass-prompts/templates/common/claude-md-scaffold.md +203 -20
- package/pass-prompts/templates/common/pass3-footer.md +297 -56
- package/pass-prompts/templates/common/pass3a-facts.md +48 -3
- package/pass-prompts/templates/common/pass4.md +78 -40
- package/pass-prompts/templates/java-spring/pass1.md +54 -0
- package/pass-prompts/templates/java-spring/pass3.md +20 -19
- package/pass-prompts/templates/kotlin-spring/pass1.md +45 -0
- package/pass-prompts/templates/kotlin-spring/pass3.md +24 -23
- package/pass-prompts/templates/node-express/pass3.md +18 -17
- package/pass-prompts/templates/node-fastify/pass3.md +11 -10
- package/pass-prompts/templates/node-nestjs/pass3.md +11 -10
- package/pass-prompts/templates/node-nextjs/pass3.md +18 -17
- package/pass-prompts/templates/node-vite/pass3.md +11 -10
- package/pass-prompts/templates/python-django/pass3.md +18 -17
- package/pass-prompts/templates/python-fastapi/pass3.md +18 -17
- package/pass-prompts/templates/python-flask/pass3.md +9 -8
- package/pass-prompts/templates/vue-nuxt/pass3.md +9 -8
- package/plan-installer/domain-grouper.js +45 -5
- package/plan-installer/index.js +34 -1
- package/plan-installer/pass3-context-builder.js +14 -0
- package/plan-installer/scanners/scan-frontend.js +2 -1
- package/plan-installer/scanners/scan-java.js +98 -2
- package/plan-installer/source-paths.js +242 -0
- package/plan-installer/stack-detector.js +522 -42
|
@@ -409,7 +409,7 @@ async function main() {
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
// ─── 10. Path-claim verification ──────────────────────────
|
|
412
|
-
// Catches two
|
|
412
|
+
// Catches two failure classes:
|
|
413
413
|
// (a) Pass 3 hallucinations: rules/standard files reference
|
|
414
414
|
// src/... paths the LLM fabricated from directory context
|
|
415
415
|
// (e.g., `src/feature/routers/featureRoutePath.ts` when the actual
|
|
@@ -427,9 +427,118 @@ async function main() {
|
|
|
427
427
|
// inline code already fenced. We still strip fenced blocks first so
|
|
428
428
|
// example blocks inside ```...``` don't produce false positives.
|
|
429
429
|
const SRC_PATH_RE = /\bsrc\/[\w\-./]+\.(?:ts|tsx|js|jsx)\b/g;
|
|
430
|
-
|
|
431
|
-
//
|
|
432
|
-
|
|
430
|
+
|
|
431
|
+
// Placeholder paths are scaffold templates / teaching examples, not
|
|
432
|
+
// real path claims. We skip them. Three patterns qualify:
|
|
433
|
+
// 1. Curly-brace placeholder — `src/{domain}/api/{Entity}.ts`
|
|
434
|
+
// (the original v2.3.0 form).
|
|
435
|
+
// 2. Xxx-style placeholder — `src/hooks/useXxx.ts` or
|
|
436
|
+
// `src/pages/PageXxx.tsx` (LLMs commonly use `Xxx`/`XXX` as
|
|
437
|
+
// "insert-your-name-here"; this form has been observed in
|
|
438
|
+
// `naming-conventions-rules.md`). We match any segment
|
|
439
|
+
// containing `Xxx` or `XXX` as a distinctive token.
|
|
440
|
+
// 3. Glob-star placeholder — `src/test/*.setup.ts` or
|
|
441
|
+
// `src/*/mocks/handlers.ts` (conventional glob syntax that an
|
|
442
|
+
// LLM may use to describe a *class* of files rather than one
|
|
443
|
+
// specific file).
|
|
444
|
+
// Literal paths that happen to contain `x`/`X` as part of a real
|
|
445
|
+
// identifier (e.g., `src/utils/xyzParser.ts`) do NOT match these
|
|
446
|
+
// patterns — the placeholder regex requires the `Xxx` pattern
|
|
447
|
+
// (capital-lower-lower, a distinctive convention) OR three or more
|
|
448
|
+
// consecutive uppercase X's. The uppercase-XXX rule has NO word-
|
|
449
|
+
// boundary anchor because placeholder tokens commonly appear in
|
|
450
|
+
// the middle of a compound identifier (`useXXX`, `useXXX_CONFIG`,
|
|
451
|
+
// `nameXXXvalue`, `XXXParser`) — requiring a boundary would skip
|
|
452
|
+
// those cases. Three consecutive uppercase X's in a row is a
|
|
453
|
+
// distinctive signal that essentially never appears in ordinary
|
|
454
|
+
// identifiers (lowercase `xxx` CAN appear in words like `taxXxxRate`,
|
|
455
|
+
// but three uppercase X's do not occur outside placeholder convention).
|
|
456
|
+
// v2.4.0 — Ellipsis (`...`) added as a placeholder marker.
|
|
457
|
+
//
|
|
458
|
+
// LLMs commonly use `...` to denote "any subdirectory" in illustrative
|
|
459
|
+
// path examples, e.g. `src/app/api/.../route.ts` to mean "any API
|
|
460
|
+
// route under app/api/". Treating these as STALE_PATH false-positives
|
|
461
|
+
// because `...` doesn't match the literal directory regex `[^/]+\.`,
|
|
462
|
+
// and because `...` can never be a real directory name (filesystems
|
|
463
|
+
// refuse it on most platforms — `.` and `..` are the only legal
|
|
464
|
+
// dot-only directory names). Three consecutive dots in a path segment
|
|
465
|
+
// are unambiguous placeholder signal.
|
|
466
|
+
const hasPlaceholder = (p) =>
|
|
467
|
+
/\{[^}]+\}/.test(p) || // {domain} style
|
|
468
|
+
/X{3,}/.test(p) || /Xxx/.test(p) || // XXX+ anywhere, or Xxx token
|
|
469
|
+
/\*/.test(p) || // glob star
|
|
470
|
+
/\/\.\.\.\//.test(p); // /.../ ellipsis path segment (v2.4.0)
|
|
471
|
+
|
|
472
|
+
// File-level exclusion: some generated rule files are DESIGNED to cite
|
|
473
|
+
// convention-trap paths as teaching examples — they tell the reader
|
|
474
|
+
// "don't invent paths like these". `content-validator`'s path-claim
|
|
475
|
+
// check is content-blind, so literal example paths inside such a file
|
|
476
|
+
// would be flagged as STALE_PATH even though the author intentionally
|
|
477
|
+
// listed them as cautionary illustrations.
|
|
478
|
+
//
|
|
479
|
+
// The exclusion is strictly opt-in, named by relative path, and
|
|
480
|
+
// limited to files whose purpose is educational-about-paths. Adding a
|
|
481
|
+
// file here is a deliberate design choice — the alternative is for
|
|
482
|
+
// the LLM to rewrite those examples as placeholders (Xxx / glob /
|
|
483
|
+
// prose), which the prompt now nudges toward but cannot strictly
|
|
484
|
+
// enforce.
|
|
485
|
+
//
|
|
486
|
+
// Current exclusions:
|
|
487
|
+
// - 00.core/52.ai-work-rules.md — the canonical "AI work rules"
|
|
488
|
+
// file, which by design lists convention-trap paths as warnings
|
|
489
|
+
// to future AI sessions. This file has been observed to
|
|
490
|
+
// accumulate STALE_PATH false positives when a prompt-level
|
|
491
|
+
// denylist primed the LLM to cite those exact paths as
|
|
492
|
+
// educational examples (the denylist has since been removed;
|
|
493
|
+
// this exclusion is the validator-side defense-in-depth).
|
|
494
|
+
// - 00.core/51.doc-writing-rules.md — the documentation writing
|
|
495
|
+
// rules file (v2.4.0). Same meta-doc class as 52.ai-work-rules.md:
|
|
496
|
+
// it teaches "verify file paths before writing them in documents"
|
|
497
|
+
// and naturally cites example paths (`src/middleware.ts`,
|
|
498
|
+
// `src/app/api/<route>/route.ts`) as illustrations of the rule.
|
|
499
|
+
// Those examples are NOT path claims — they are the lesson. The
|
|
500
|
+
// content-blind validator would otherwise flag every cited example
|
|
501
|
+
// as STALE_PATH on every project that doesn't happen to contain
|
|
502
|
+
// all the cited illustrative files. Excluded for the same reason
|
|
503
|
+
// and via the same mechanism as 52.ai-work-rules.md.
|
|
504
|
+
const PATH_CLAIM_EXCLUDE_FILES = new Set([
|
|
505
|
+
"00.core/52.ai-work-rules.md",
|
|
506
|
+
"00.core/51.doc-writing-rules.md",
|
|
507
|
+
]);
|
|
508
|
+
|
|
509
|
+
// v2.4.0 — Resolve a `src/...` path claim against monorepo workspaces.
|
|
510
|
+
//
|
|
511
|
+
// Pre-v2.4.0 the validator only checked `<ROOT>/<claimed>` directly,
|
|
512
|
+
// which produced false-positive STALE_PATH advisories on Turborepo /
|
|
513
|
+
// pnpm-workspace projects where source files live under
|
|
514
|
+
// `apps/<app-name>/src/...` or `packages/<pkg-name>/src/...`. A rule
|
|
515
|
+
// citing `src/app/layout.tsx` is the natural single-app shorthand
|
|
516
|
+
// even when the actual file is at `apps/<app-name>/src/app/layout.tsx`.
|
|
517
|
+
//
|
|
518
|
+
// Resolution order (first match wins):
|
|
519
|
+
// 1. Direct: `<ROOT>/<claimed>` (single-app project)
|
|
520
|
+
// 2. Monorepo apps: `<ROOT>/apps/*/<claimed>`
|
|
521
|
+
// 3. Monorepo packages: `<ROOT>/packages/*/<claimed>`
|
|
522
|
+
//
|
|
523
|
+
// Returns true on first match, false if no match found anywhere.
|
|
524
|
+
// The monorepo fallback only fires for paths starting with `src/`,
|
|
525
|
+
// which is the conventional workspace-relative form. Non-`src/` paths
|
|
526
|
+
// (e.g., `claudeos-core/skills/...`) are checked direct-only.
|
|
527
|
+
function resolvePathClaim(ROOT, claimed) {
|
|
528
|
+
if (fs.existsSync(path.join(ROOT, claimed))) return true;
|
|
529
|
+
if (!claimed.startsWith("src/")) return false;
|
|
530
|
+
for (const workspace of ["apps", "packages"]) {
|
|
531
|
+
const wsDir = path.join(ROOT, workspace);
|
|
532
|
+
let entries;
|
|
533
|
+
try { entries = fs.readdirSync(wsDir, { withFileTypes: true }); }
|
|
534
|
+
catch (_e) { continue; }
|
|
535
|
+
for (const entry of entries) {
|
|
536
|
+
if (!entry.isDirectory()) continue;
|
|
537
|
+
if (fs.existsSync(path.join(wsDir, entry.name, claimed))) return true;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
433
542
|
|
|
434
543
|
// Strip fenced code blocks (``` and ~~~) so examples inside code
|
|
435
544
|
// blocks don't trigger the check — they're illustrations, not claims.
|
|
@@ -459,10 +568,19 @@ async function main() {
|
|
|
459
568
|
];
|
|
460
569
|
let pathClaimsChecked = 0;
|
|
461
570
|
let pathClaimErrors = 0;
|
|
571
|
+
let pathClaimFilesExcluded = 0;
|
|
462
572
|
for (const target of pathClaimTargets) {
|
|
463
573
|
if (!fs.existsSync(target.dir)) continue;
|
|
464
574
|
const files = await glob(target.glob, { cwd: target.dir, absolute: true });
|
|
465
575
|
for (const file of files) {
|
|
576
|
+
// File-level exclusion: the path is relative to the target dir
|
|
577
|
+
// (e.g., "00.core/52.ai-work-rules.md" inside .claude/rules/).
|
|
578
|
+
// Normalize to forward slashes for cross-platform match.
|
|
579
|
+
const relToTargetDir = path.relative(target.dir, file).split(path.sep).join("/");
|
|
580
|
+
if (PATH_CLAIM_EXCLUDE_FILES.has(relToTargetDir)) {
|
|
581
|
+
pathClaimFilesExcluded++;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
466
584
|
const raw = fs.readFileSync(file, "utf-8");
|
|
467
585
|
const stripped = stripFences(raw);
|
|
468
586
|
const seen = new Set(); // dedupe within a single file
|
|
@@ -474,8 +592,7 @@ async function main() {
|
|
|
474
592
|
seen.add(claimed);
|
|
475
593
|
if (hasPlaceholder(claimed)) continue;
|
|
476
594
|
pathClaimsChecked++;
|
|
477
|
-
|
|
478
|
-
if (!fs.existsSync(absolutePath)) {
|
|
595
|
+
if (!resolvePathClaim(ROOT, claimed)) {
|
|
479
596
|
pathClaimErrors++;
|
|
480
597
|
errors.push({
|
|
481
598
|
file: rel(file),
|
|
@@ -488,7 +605,8 @@ async function main() {
|
|
|
488
605
|
}
|
|
489
606
|
}
|
|
490
607
|
}
|
|
491
|
-
console.log(` ${pathClaimsChecked} path claim(s) checked, ${pathClaimErrors} stale`
|
|
608
|
+
console.log(` ${pathClaimsChecked} path claim(s) checked, ${pathClaimErrors} stale` +
|
|
609
|
+
(pathClaimFilesExcluded > 0 ? ` (${pathClaimFilesExcluded} file(s) excluded by design)` : ""));
|
|
492
610
|
|
|
493
611
|
// MANIFEST ↔ CLAUDE.md §6 Skills drift check.
|
|
494
612
|
// MANIFEST registers skills in a 4-column table; each row's second
|
|
@@ -583,8 +701,13 @@ async function main() {
|
|
|
583
701
|
// file of the form `skills/{category}/*{parent}*.md` (excluding
|
|
584
702
|
// the sub-skill itself) counts as a plausible orchestrator.
|
|
585
703
|
function orchestratorFor(subSkillPath) {
|
|
704
|
+
// Sub-skill path forms accepted (v2.4.0 generalization):
|
|
705
|
+
// skills/{category}/{parent-stem}/NN.{name}.md (legacy NN. prefix)
|
|
706
|
+
// skills/{category}/{parent-stem}/SKILL.md (v2.4.0 SKILL.md convention)
|
|
707
|
+
// skills/{category}/{parent-stem}/{name}.md (no NN. prefix)
|
|
708
|
+
// Captures `parent-stem` and the category directory.
|
|
586
709
|
const m = subSkillPath.match(
|
|
587
|
-
/^(claudeos-core\/skills\/[^/]+\/)([^/]+)
|
|
710
|
+
/^(claudeos-core\/skills\/[^/]+\/)([^/]+)\/(?:\d+\.)?[^/]+\.md$/
|
|
588
711
|
);
|
|
589
712
|
if (!m) return null;
|
|
590
713
|
return { categoryDir: m[1], stem: m[2] };
|
|
@@ -594,15 +717,47 @@ async function main() {
|
|
|
594
717
|
// basename (minus leading number + dot) matches the sub-skill
|
|
595
718
|
// parent stem. This accepts `01.scaffold-crud-feature.md`,
|
|
596
719
|
// `scaffold-crud-feature.md`, etc.
|
|
720
|
+
// v2.4.0: a category-level `SKILL.md` (the orchestrator file
|
|
721
|
+
// colocated with `{category}/SKILL.md`) is treated as the
|
|
722
|
+
// orchestrator for ALL sub-skills under that category — this
|
|
723
|
+
// matches the new generator convention where each category has
|
|
724
|
+
// a single top-level orchestrator at `{category}/SKILL.md`.
|
|
597
725
|
if (!ref.startsWith(categoryDir)) return false;
|
|
598
726
|
const tail = ref.slice(categoryDir.length);
|
|
599
727
|
// Must be a sibling file, not a nested path.
|
|
600
728
|
if (tail.includes("/")) return false;
|
|
729
|
+
// v2.4.0 SKILL.md convention: a category-level SKILL.md covers
|
|
730
|
+
// every sub-skill in that category.
|
|
731
|
+
if (tail === "SKILL.md") return true;
|
|
601
732
|
// Strip leading "NN." if present, then compare stem.
|
|
602
733
|
const base = tail.replace(/^\d+\./, "").replace(/\.md$/, "");
|
|
603
734
|
return base === stem;
|
|
604
735
|
}
|
|
605
736
|
|
|
737
|
+
// Pre-compute: is any MANIFEST.md (the global skill registry)
|
|
738
|
+
// referenced anywhere in CLAUDE.md? Used as an additional
|
|
739
|
+
// sub-skill coverage rule below.
|
|
740
|
+
//
|
|
741
|
+
// Observed scenario: Pass 3c sometimes invents new sub-skill
|
|
742
|
+
// folder structures (e.g. `{category}/domains/{domain}.md` for
|
|
743
|
+
// per-domain notes) that weren't anticipated by the orchestrator
|
|
744
|
+
// pattern (which expects `{category}/{parent-stem}/{name}.md` paired
|
|
745
|
+
// with `{category}/{parent-stem}.md`). When this happens, every new
|
|
746
|
+
// sub-skill registration drifts because no sibling orchestrator exists.
|
|
747
|
+
//
|
|
748
|
+
// The architectural intent is that MANIFEST.md IS the registry — if
|
|
749
|
+
// CLAUDE.md §6 tells the reader "see MANIFEST.md for the full list",
|
|
750
|
+
// the reader can navigate to find every sub-skill. So mentioning
|
|
751
|
+
// MANIFEST.md anywhere in CLAUDE.md covers ALL sub-skill paths
|
|
752
|
+
// transitively. Top-level skills (direct `{category}/{file}.md`
|
|
753
|
+
// entries — those that don't match `orchestratorFor`) still require
|
|
754
|
+
// direct mention; this exception only applies to deep paths.
|
|
755
|
+
//
|
|
756
|
+
// Note: `referenced` Set above filters out MANIFEST.md entries (line
|
|
757
|
+
// 666), so we must scan the raw mdStripped text directly to detect
|
|
758
|
+
// MANIFEST.md mention.
|
|
759
|
+
const manifestReferencedGlobally = /`claudeos-core\/skills\/[\w\-./]*MANIFEST\.md`/.test(mdStripped);
|
|
760
|
+
|
|
606
761
|
for (const p of registered) {
|
|
607
762
|
if (referenced.has(p)) continue; // direct mention → OK
|
|
608
763
|
|
|
@@ -613,6 +768,7 @@ async function main() {
|
|
|
613
768
|
isOrchestratorReferenced(ref, oc)
|
|
614
769
|
);
|
|
615
770
|
if (orchestratorMentioned) continue; // covered via orchestrator
|
|
771
|
+
if (manifestReferencedGlobally) continue; // covered via global MANIFEST
|
|
616
772
|
}
|
|
617
773
|
|
|
618
774
|
manifestErrors++;
|
|
@@ -634,14 +790,26 @@ async function main() {
|
|
|
634
790
|
}
|
|
635
791
|
|
|
636
792
|
// ─── Output results ─────────────────────────────────────────
|
|
793
|
+
//
|
|
794
|
+
// Terminology note (v2.3.3): the internal arrays stay named `errors` and
|
|
795
|
+
// `warnings` because they encode *severity* for programmatic consumers
|
|
796
|
+
// (health-checker's pass/fail gate, stale-report.json's schema, CI
|
|
797
|
+
// pipelines). The user-visible labels, however, are "advisories" and
|
|
798
|
+
// "notes" — because these are quality observations about LLM-generated
|
|
799
|
+
// documents, not test failures. A STALE_PATH doesn't mean `init` crashed;
|
|
800
|
+
// it means "AI guessed a filename that doesn't exist on disk — worth
|
|
801
|
+
// reviewing". Calling that an "error" made users think generation had
|
|
802
|
+
// actually failed. The exit code behavior is unchanged — this tool still
|
|
803
|
+
// returns 1 when advisories exist so `npx claudeos-core health` remains
|
|
804
|
+
// a real gate for CI.
|
|
637
805
|
console.log(`\n Checked ${checked} files\n`);
|
|
638
806
|
if (errors.length) {
|
|
639
|
-
console.log(`
|
|
807
|
+
console.log(` ℹ️ ADVISORIES (${errors.length}):`);
|
|
640
808
|
errors.forEach(e => console.log(` [${e.type}] ${e.file}: ${e.msg}`));
|
|
641
809
|
console.log();
|
|
642
810
|
}
|
|
643
811
|
if (warnings.length) {
|
|
644
|
-
console.log(` ⚠️
|
|
812
|
+
console.log(` ⚠️ NOTES (${warnings.length}):`);
|
|
645
813
|
warnings.forEach(w => console.log(` [${w.type}] ${w.file}: ${w.msg}`));
|
|
646
814
|
console.log();
|
|
647
815
|
}
|
|
@@ -649,13 +817,18 @@ async function main() {
|
|
|
649
817
|
console.log(" ✅ All content validation passed\n");
|
|
650
818
|
}
|
|
651
819
|
|
|
652
|
-
// Record in stale-report
|
|
820
|
+
// Record in stale-report. Field names (`contentErrors`, `contentWarnings`)
|
|
821
|
+
// stay stable because they are part of stale-report.json's public schema
|
|
822
|
+
// that health-checker and external CI consumers read.
|
|
653
823
|
updateStaleReport(GEN_DIR, "contentValidation",
|
|
654
824
|
{ checkedAt: new Date().toISOString(), checked, errors: errors.length, warnings: warnings.length, details: { errors, warnings } },
|
|
655
825
|
{ contentErrors: errors.length, contentWarnings: warnings.length }
|
|
656
826
|
);
|
|
657
827
|
|
|
658
|
-
console.log(` Total: ${errors.length}
|
|
828
|
+
console.log(` Total: ${errors.length} advisories, ${warnings.length} notes\n`);
|
|
829
|
+
// Exit code preserved (advisories → 1) so health-checker can still gate
|
|
830
|
+
// on this tool. The `init` orchestrator displays the result as a soft
|
|
831
|
+
// advisory regardless (see runContentValidator in bin/commands/init.js).
|
|
659
832
|
process.exit(errors.length > 0 ? 1 : 0);
|
|
660
833
|
}
|
|
661
834
|
|
package/health-checker/index.js
CHANGED
|
@@ -61,11 +61,29 @@ function main() {
|
|
|
61
61
|
console.log();
|
|
62
62
|
|
|
63
63
|
// ─── [1-4] Run verification tools sequentially ────────────────────
|
|
64
|
+
//
|
|
65
|
+
// Tool status tiers (3-way):
|
|
66
|
+
// - default : non-zero exit → "fail" (❌, sets hasErr, blocks `health` exit code)
|
|
67
|
+
// - warnOnly:true : non-zero exit → "warn" (⚠️, does not set hasErr)
|
|
68
|
+
// - softFail:true : non-zero exit → "advisory" (ℹ️, does not set hasErr)
|
|
69
|
+
//
|
|
70
|
+
// The `softFail` tier (v2.4.0) was added for `content-validator` after
|
|
71
|
+
// user feedback: its findings are documentation quality notes
|
|
72
|
+
// (STALE_PATH suggestions, MANIFEST_DRIFT, NO_BAD_EXAMPLE) not generation
|
|
73
|
+
// failures, but pre-fix it surfaced as "❌ fail" alongside the
|
|
74
|
+
// "non-fatal" message — a confusing dual signal. `ℹ️ advisory` separates
|
|
75
|
+
// the visual from real structural failures (plan-validator,
|
|
76
|
+
// sync-checker, manifest-generator).
|
|
77
|
+
//
|
|
78
|
+
// `warnOnly` (existing) and `softFail` (new) are functionally similar at
|
|
79
|
+
// the gate level; the tier name encodes intent: warn = "watch this",
|
|
80
|
+
// advisory = "review when convenient". Both keep the health-check gate
|
|
81
|
+
// green.
|
|
64
82
|
const tools = [
|
|
65
|
-
{ name: "plan-validator", script: path.join(TOOLS, "plan-validator/index.js"), desc: "Plan consistency"
|
|
66
|
-
{ name: "sync-checker", script: path.join(TOOLS, "sync-checker/index.js"), desc: "Sync status"
|
|
67
|
-
{ name: "content-validator", script: path.join(TOOLS, "content-validator/index.js"), desc: "Content quality" },
|
|
68
|
-
{ name: "pass-json-validator", script: path.join(TOOLS, "pass-json-validator/index.js"), desc: "JSON format",
|
|
83
|
+
{ name: "plan-validator", script: path.join(TOOLS, "plan-validator/index.js"), desc: "Plan consistency" },
|
|
84
|
+
{ name: "sync-checker", script: path.join(TOOLS, "sync-checker/index.js"), desc: "Sync status" },
|
|
85
|
+
{ name: "content-validator", script: path.join(TOOLS, "content-validator/index.js"), desc: "Content quality", softFail: true },
|
|
86
|
+
{ name: "pass-json-validator", script: path.join(TOOLS, "pass-json-validator/index.js"), desc: "JSON format", warnOnly: true },
|
|
69
87
|
];
|
|
70
88
|
|
|
71
89
|
const results = [];
|
|
@@ -88,6 +106,9 @@ function main() {
|
|
|
88
106
|
if (r.ok) {
|
|
89
107
|
console.log(" ✅");
|
|
90
108
|
results.push({ name: t.name, status: "pass" });
|
|
109
|
+
} else if (t.softFail) {
|
|
110
|
+
console.log(" ℹ️");
|
|
111
|
+
results.push({ name: t.name, status: "advisory" });
|
|
91
112
|
} else if (t.warnOnly) {
|
|
92
113
|
console.log(" ⚠️");
|
|
93
114
|
results.push({ name: t.name, status: "warn" });
|
|
@@ -101,15 +122,28 @@ function main() {
|
|
|
101
122
|
// ─── Results summary ──────────────────────────────────────────
|
|
102
123
|
console.log("\n ══════════════════════════════");
|
|
103
124
|
results.forEach((r) => {
|
|
104
|
-
const icon = r.status === "pass" ? "✅"
|
|
125
|
+
const icon = r.status === "pass" ? "✅"
|
|
126
|
+
: r.status === "fail" ? "❌"
|
|
127
|
+
: r.status === "warn" ? "⚠️"
|
|
128
|
+
: r.status === "advisory" ? "ℹ️"
|
|
129
|
+
: "⏭️";
|
|
105
130
|
console.log(` ${icon} ${r.name.padEnd(22)} ${r.status}`);
|
|
106
131
|
});
|
|
107
132
|
console.log(" ──────────────────────────────");
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
);
|
|
133
|
+
// Summary line: distinguish real failures (block the gate) from
|
|
134
|
+
// advisory/warn results (informational, gate stays green).
|
|
135
|
+
const failCount = results.filter((r) => r.status === "fail").length;
|
|
136
|
+
const advisoryCount = results.filter((r) => r.status === "advisory").length;
|
|
137
|
+
const warnCount = results.filter((r) => r.status === "warn").length;
|
|
138
|
+
if (hasErr) {
|
|
139
|
+
console.log(` ⚠️ ${failCount} failed`);
|
|
140
|
+
} else {
|
|
141
|
+
const tail = [];
|
|
142
|
+
if (advisoryCount) tail.push(`${advisoryCount} advisory`);
|
|
143
|
+
if (warnCount) tail.push(`${warnCount} warning`);
|
|
144
|
+
const suffix = tail.length ? ` (${tail.join(", ")})` : "";
|
|
145
|
+
console.log(` ✅ All systems operational${suffix}`);
|
|
146
|
+
}
|
|
113
147
|
console.log(" ══════════════════════════════\n");
|
|
114
148
|
|
|
115
149
|
// ─── Update stale-report.json ────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,90 +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
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"README.
|
|
30
|
-
"README.
|
|
31
|
-
"README.
|
|
32
|
-
"README.
|
|
33
|
-
"README.
|
|
34
|
-
"README.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"code
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "claudeos-core",
|
|
3
|
+
"version": "2.4.0",
|
|
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
|
+
}
|