guardian-framework 0.1.143 → 0.1.144
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/dist/cli.js
CHANGED
|
@@ -3206,7 +3206,7 @@ __export(exports_package, {
|
|
|
3206
3206
|
bin: () => bin,
|
|
3207
3207
|
author: () => author
|
|
3208
3208
|
});
|
|
3209
|
-
var name = "guardian-framework", version = "0.1.
|
|
3209
|
+
var name = "guardian-framework", version = "0.1.144", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
3210
3210
|
var init_package = __esm(() => {
|
|
3211
3211
|
exports = {
|
|
3212
3212
|
".": {
|
|
@@ -11625,6 +11625,117 @@ import * as path3 from "path";
|
|
|
11625
11625
|
import * as crypto from "crypto";
|
|
11626
11626
|
import * as fs from "fs";
|
|
11627
11627
|
import * as path2 from "path";
|
|
11628
|
+
|
|
11629
|
+
// src/lib/language-stubs.ts
|
|
11630
|
+
var FENCE = {
|
|
11631
|
+
typescript: "typescript",
|
|
11632
|
+
nextjs: "typescript",
|
|
11633
|
+
angular: "typescript",
|
|
11634
|
+
rust: "rust",
|
|
11635
|
+
python: "python",
|
|
11636
|
+
go: "go",
|
|
11637
|
+
java: "java"
|
|
11638
|
+
};
|
|
11639
|
+
function normalizeStubLanguage(language) {
|
|
11640
|
+
const l2 = (language ?? "").trim().toLowerCase();
|
|
11641
|
+
if (l2 === "nextjs" || l2 === "angular" || l2 === "typescript")
|
|
11642
|
+
return "typescript";
|
|
11643
|
+
if (l2 === "rust" || l2 === "python" || l2 === "go" || l2 === "java")
|
|
11644
|
+
return l2;
|
|
11645
|
+
return "neutral";
|
|
11646
|
+
}
|
|
11647
|
+
function renderRepositoryInterface(language, aggregateName) {
|
|
11648
|
+
const lang = normalizeStubLanguage(language);
|
|
11649
|
+
const fence = FENCE[lang] ?? "text";
|
|
11650
|
+
const id = `${aggregateName}Id`;
|
|
11651
|
+
if (lang === "rust") {
|
|
11652
|
+
return [
|
|
11653
|
+
`\`\`\`${fence}`,
|
|
11654
|
+
`pub trait ${aggregateName}Repository: Send + Sync {`,
|
|
11655
|
+
` async fn find_by_id(&self, id: &${id}) -> Result<Option<${aggregateName}>, DomainError>;`,
|
|
11656
|
+
` async fn save(&self, ${lowerFirst(aggregateName)}: &${aggregateName}) -> Result<(), DomainError>;`,
|
|
11657
|
+
` async fn delete(&self, id: &${id}) -> Result<(), DomainError>;`,
|
|
11658
|
+
"}",
|
|
11659
|
+
"```"
|
|
11660
|
+
].join(`
|
|
11661
|
+
`);
|
|
11662
|
+
}
|
|
11663
|
+
if (lang === "python") {
|
|
11664
|
+
return [
|
|
11665
|
+
`\`\`\`${fence}`,
|
|
11666
|
+
"from abc import ABC, abstractmethod",
|
|
11667
|
+
"",
|
|
11668
|
+
"",
|
|
11669
|
+
`class ${aggregateName}Repository(ABC):`,
|
|
11670
|
+
` """Persistence port for the ${aggregateName} aggregate (implemented in infrastructure)."""`,
|
|
11671
|
+
"",
|
|
11672
|
+
" @abstractmethod",
|
|
11673
|
+
` async def find_by_id(self, id_: ${id}) -> ${aggregateName} | None: ...`,
|
|
11674
|
+
"",
|
|
11675
|
+
" @abstractmethod",
|
|
11676
|
+
` async def save(self, ${snake(aggregateName)}: ${aggregateName}) -> None: ...`,
|
|
11677
|
+
"",
|
|
11678
|
+
" @abstractmethod",
|
|
11679
|
+
` async def delete(self, id_: ${id}) -> None: ...`,
|
|
11680
|
+
"```"
|
|
11681
|
+
].join(`
|
|
11682
|
+
`);
|
|
11683
|
+
}
|
|
11684
|
+
if (lang === "java") {
|
|
11685
|
+
return [
|
|
11686
|
+
`\`\`\`${fence}`,
|
|
11687
|
+
`public interface ${aggregateName}Repository {`,
|
|
11688
|
+
` Optional<${aggregateName}> findById(${id} id);`,
|
|
11689
|
+
` void save(${aggregateName} ${lowerFirst(aggregateName)});`,
|
|
11690
|
+
` void delete(${id} id);`,
|
|
11691
|
+
"}",
|
|
11692
|
+
"```"
|
|
11693
|
+
].join(`
|
|
11694
|
+
`);
|
|
11695
|
+
}
|
|
11696
|
+
if (lang === "go") {
|
|
11697
|
+
return [
|
|
11698
|
+
`\`\`\`${fence}`,
|
|
11699
|
+
`// ${aggregateName}Repository is the persistence port for the ${aggregateName} aggregate.`,
|
|
11700
|
+
`type ${aggregateName}Repository interface {`,
|
|
11701
|
+
` FindByID(ctx context.Context, id ${id}) (*${aggregateName}, error)`,
|
|
11702
|
+
` Save(ctx context.Context, ${lowerFirst(aggregateName)} *${aggregateName}) error`,
|
|
11703
|
+
` Delete(ctx context.Context, id ${id}) error`,
|
|
11704
|
+
"}",
|
|
11705
|
+
"```"
|
|
11706
|
+
].join(`
|
|
11707
|
+
`);
|
|
11708
|
+
}
|
|
11709
|
+
if (lang === "typescript") {
|
|
11710
|
+
return [
|
|
11711
|
+
`\`\`\`${fence}`,
|
|
11712
|
+
`export interface ${aggregateName}Repository {`,
|
|
11713
|
+
` findById(id: ${id}): Promise<${aggregateName} | null>;`,
|
|
11714
|
+
` save(${lowerFirst(aggregateName)}: ${aggregateName}): Promise<void>;`,
|
|
11715
|
+
` delete(id: ${id}): Promise<void>;`,
|
|
11716
|
+
"}",
|
|
11717
|
+
"```"
|
|
11718
|
+
].join(`
|
|
11719
|
+
`);
|
|
11720
|
+
}
|
|
11721
|
+
return [
|
|
11722
|
+
`\`\`\`${fence}`,
|
|
11723
|
+
`${aggregateName}Repository (persistence port)`,
|
|
11724
|
+
` findById(id: ${id}) -> ${aggregateName} | null`,
|
|
11725
|
+
` save(${aggregateName}) -> void`,
|
|
11726
|
+
` delete(id: ${id}) -> void`,
|
|
11727
|
+
"```"
|
|
11728
|
+
].join(`
|
|
11729
|
+
`);
|
|
11730
|
+
}
|
|
11731
|
+
function lowerFirst(s) {
|
|
11732
|
+
return s.length > 0 ? s.charAt(0).toLowerCase() + s.slice(1) : s;
|
|
11733
|
+
}
|
|
11734
|
+
function snake(s) {
|
|
11735
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
11736
|
+
}
|
|
11737
|
+
|
|
11738
|
+
// src/lib/domain-explorer.ts
|
|
11628
11739
|
var __dirname = "/Users/arman/project/guardian-framework/src/lib", __filename = "/Users/arman/project/guardian-framework/src/lib/domain-explorer.ts";
|
|
11629
11740
|
var MAX_BUSINESS_CONTEXT_LENGTH = 5000;
|
|
11630
11741
|
function sanitizeBusinessContext(context) {
|
|
@@ -12793,23 +12904,11 @@ ${bc.description}
|
|
|
12793
12904
|
content += `
|
|
12794
12905
|
`;
|
|
12795
12906
|
}
|
|
12796
|
-
const arVarName = ar.name.charAt(0).toLowerCase() + ar.name.slice(1);
|
|
12797
12907
|
content += `**Repository Interface:**
|
|
12798
12908
|
`;
|
|
12799
12909
|
content += `
|
|
12800
12910
|
`;
|
|
12801
|
-
content +=
|
|
12802
|
-
content += `pub trait ${ar.name}Repository: Send + Sync {
|
|
12803
|
-
`;
|
|
12804
|
-
content += ` async fn find_by_id(&self, id: &${ar.name}Id) -> Result<Option<${ar.name}>, DomainError>;
|
|
12805
|
-
`;
|
|
12806
|
-
content += ` async fn save(&self, ${arVarName}: &${ar.name}) -> Result<(), DomainError>;
|
|
12807
|
-
`;
|
|
12808
|
-
content += ` async fn delete(&self, id: &${ar.name}Id) -> Result<(), DomainError>;
|
|
12809
|
-
`;
|
|
12810
|
-
content += `}
|
|
12811
|
-
`;
|
|
12812
|
-
content += "```";
|
|
12911
|
+
content += renderRepositoryInterface(lang, ar.name);
|
|
12813
12912
|
content += `
|
|
12814
12913
|
`;
|
|
12815
12914
|
if (ownedEntities.length > 0 || ownedVOs.length > 0 || invariants.length > 0) {
|
package/dist/exports.js
CHANGED
|
@@ -1991,7 +1991,7 @@ __export(exports_package, {
|
|
|
1991
1991
|
bin: () => bin,
|
|
1992
1992
|
author: () => author
|
|
1993
1993
|
});
|
|
1994
|
-
var name = "guardian-framework", version = "0.1.
|
|
1994
|
+
var name = "guardian-framework", version = "0.1.144", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
1995
1995
|
var init_package = __esm(() => {
|
|
1996
1996
|
exports = {
|
|
1997
1997
|
".": {
|
|
@@ -11948,6 +11948,117 @@ import * as path12 from "node:path";
|
|
|
11948
11948
|
import * as crypto4 from "node:crypto";
|
|
11949
11949
|
import * as fs12 from "node:fs";
|
|
11950
11950
|
import * as path11 from "node:path";
|
|
11951
|
+
|
|
11952
|
+
// src/lib/language-stubs.ts
|
|
11953
|
+
var FENCE = {
|
|
11954
|
+
typescript: "typescript",
|
|
11955
|
+
nextjs: "typescript",
|
|
11956
|
+
angular: "typescript",
|
|
11957
|
+
rust: "rust",
|
|
11958
|
+
python: "python",
|
|
11959
|
+
go: "go",
|
|
11960
|
+
java: "java"
|
|
11961
|
+
};
|
|
11962
|
+
function normalizeStubLanguage(language) {
|
|
11963
|
+
const l2 = (language ?? "").trim().toLowerCase();
|
|
11964
|
+
if (l2 === "nextjs" || l2 === "angular" || l2 === "typescript")
|
|
11965
|
+
return "typescript";
|
|
11966
|
+
if (l2 === "rust" || l2 === "python" || l2 === "go" || l2 === "java")
|
|
11967
|
+
return l2;
|
|
11968
|
+
return "neutral";
|
|
11969
|
+
}
|
|
11970
|
+
function renderRepositoryInterface(language, aggregateName) {
|
|
11971
|
+
const lang = normalizeStubLanguage(language);
|
|
11972
|
+
const fence = FENCE[lang] ?? "text";
|
|
11973
|
+
const id = `${aggregateName}Id`;
|
|
11974
|
+
if (lang === "rust") {
|
|
11975
|
+
return [
|
|
11976
|
+
`\`\`\`${fence}`,
|
|
11977
|
+
`pub trait ${aggregateName}Repository: Send + Sync {`,
|
|
11978
|
+
` async fn find_by_id(&self, id: &${id}) -> Result<Option<${aggregateName}>, DomainError>;`,
|
|
11979
|
+
` async fn save(&self, ${lowerFirst(aggregateName)}: &${aggregateName}) -> Result<(), DomainError>;`,
|
|
11980
|
+
` async fn delete(&self, id: &${id}) -> Result<(), DomainError>;`,
|
|
11981
|
+
"}",
|
|
11982
|
+
"```"
|
|
11983
|
+
].join(`
|
|
11984
|
+
`);
|
|
11985
|
+
}
|
|
11986
|
+
if (lang === "python") {
|
|
11987
|
+
return [
|
|
11988
|
+
`\`\`\`${fence}`,
|
|
11989
|
+
"from abc import ABC, abstractmethod",
|
|
11990
|
+
"",
|
|
11991
|
+
"",
|
|
11992
|
+
`class ${aggregateName}Repository(ABC):`,
|
|
11993
|
+
` """Persistence port for the ${aggregateName} aggregate (implemented in infrastructure)."""`,
|
|
11994
|
+
"",
|
|
11995
|
+
" @abstractmethod",
|
|
11996
|
+
` async def find_by_id(self, id_: ${id}) -> ${aggregateName} | None: ...`,
|
|
11997
|
+
"",
|
|
11998
|
+
" @abstractmethod",
|
|
11999
|
+
` async def save(self, ${snake(aggregateName)}: ${aggregateName}) -> None: ...`,
|
|
12000
|
+
"",
|
|
12001
|
+
" @abstractmethod",
|
|
12002
|
+
` async def delete(self, id_: ${id}) -> None: ...`,
|
|
12003
|
+
"```"
|
|
12004
|
+
].join(`
|
|
12005
|
+
`);
|
|
12006
|
+
}
|
|
12007
|
+
if (lang === "java") {
|
|
12008
|
+
return [
|
|
12009
|
+
`\`\`\`${fence}`,
|
|
12010
|
+
`public interface ${aggregateName}Repository {`,
|
|
12011
|
+
` Optional<${aggregateName}> findById(${id} id);`,
|
|
12012
|
+
` void save(${aggregateName} ${lowerFirst(aggregateName)});`,
|
|
12013
|
+
` void delete(${id} id);`,
|
|
12014
|
+
"}",
|
|
12015
|
+
"```"
|
|
12016
|
+
].join(`
|
|
12017
|
+
`);
|
|
12018
|
+
}
|
|
12019
|
+
if (lang === "go") {
|
|
12020
|
+
return [
|
|
12021
|
+
`\`\`\`${fence}`,
|
|
12022
|
+
`// ${aggregateName}Repository is the persistence port for the ${aggregateName} aggregate.`,
|
|
12023
|
+
`type ${aggregateName}Repository interface {`,
|
|
12024
|
+
` FindByID(ctx context.Context, id ${id}) (*${aggregateName}, error)`,
|
|
12025
|
+
` Save(ctx context.Context, ${lowerFirst(aggregateName)} *${aggregateName}) error`,
|
|
12026
|
+
` Delete(ctx context.Context, id ${id}) error`,
|
|
12027
|
+
"}",
|
|
12028
|
+
"```"
|
|
12029
|
+
].join(`
|
|
12030
|
+
`);
|
|
12031
|
+
}
|
|
12032
|
+
if (lang === "typescript") {
|
|
12033
|
+
return [
|
|
12034
|
+
`\`\`\`${fence}`,
|
|
12035
|
+
`export interface ${aggregateName}Repository {`,
|
|
12036
|
+
` findById(id: ${id}): Promise<${aggregateName} | null>;`,
|
|
12037
|
+
` save(${lowerFirst(aggregateName)}: ${aggregateName}): Promise<void>;`,
|
|
12038
|
+
` delete(id: ${id}): Promise<void>;`,
|
|
12039
|
+
"}",
|
|
12040
|
+
"```"
|
|
12041
|
+
].join(`
|
|
12042
|
+
`);
|
|
12043
|
+
}
|
|
12044
|
+
return [
|
|
12045
|
+
`\`\`\`${fence}`,
|
|
12046
|
+
`${aggregateName}Repository (persistence port)`,
|
|
12047
|
+
` findById(id: ${id}) -> ${aggregateName} | null`,
|
|
12048
|
+
` save(${aggregateName}) -> void`,
|
|
12049
|
+
` delete(id: ${id}) -> void`,
|
|
12050
|
+
"```"
|
|
12051
|
+
].join(`
|
|
12052
|
+
`);
|
|
12053
|
+
}
|
|
12054
|
+
function lowerFirst(s) {
|
|
12055
|
+
return s.length > 0 ? s.charAt(0).toLowerCase() + s.slice(1) : s;
|
|
12056
|
+
}
|
|
12057
|
+
function snake(s) {
|
|
12058
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
12059
|
+
}
|
|
12060
|
+
|
|
12061
|
+
// src/lib/domain-explorer.ts
|
|
11951
12062
|
var __dirname = "/Users/arman/project/guardian-framework/src/lib", __filename = "/Users/arman/project/guardian-framework/src/lib/domain-explorer.ts";
|
|
11952
12063
|
var MAX_BUSINESS_CONTEXT_LENGTH = 5000;
|
|
11953
12064
|
function sanitizeBusinessContext(context) {
|
|
@@ -13116,23 +13227,11 @@ ${bc.description}
|
|
|
13116
13227
|
content += `
|
|
13117
13228
|
`;
|
|
13118
13229
|
}
|
|
13119
|
-
const arVarName = ar.name.charAt(0).toLowerCase() + ar.name.slice(1);
|
|
13120
13230
|
content += `**Repository Interface:**
|
|
13121
13231
|
`;
|
|
13122
13232
|
content += `
|
|
13123
13233
|
`;
|
|
13124
|
-
content +=
|
|
13125
|
-
content += `pub trait ${ar.name}Repository: Send + Sync {
|
|
13126
|
-
`;
|
|
13127
|
-
content += ` async fn find_by_id(&self, id: &${ar.name}Id) -> Result<Option<${ar.name}>, DomainError>;
|
|
13128
|
-
`;
|
|
13129
|
-
content += ` async fn save(&self, ${arVarName}: &${ar.name}) -> Result<(), DomainError>;
|
|
13130
|
-
`;
|
|
13131
|
-
content += ` async fn delete(&self, id: &${ar.name}Id) -> Result<(), DomainError>;
|
|
13132
|
-
`;
|
|
13133
|
-
content += `}
|
|
13134
|
-
`;
|
|
13135
|
-
content += "```";
|
|
13234
|
+
content += renderRepositoryInterface(lang, ar.name);
|
|
13136
13235
|
content += `
|
|
13137
13236
|
`;
|
|
13138
13237
|
if (ownedEntities.length > 0 || ownedVOs.length > 0 || invariants.length > 0) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# check_language_consistency.sh — architecture docs must use the project language
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# Catches two failure modes:
|
|
6
|
+
# 1. Scaffold emitting stubs in the wrong language (historically Rust in every
|
|
7
|
+
# project, regardless of manifest language).
|
|
8
|
+
# 2. An enrichment/codegen agent copying a foreign-language sample.
|
|
9
|
+
#
|
|
10
|
+
# Fences for "infrastructure" languages that are legitimately language-agnostic
|
|
11
|
+
# (json, yaml, toml, bash, sql, mermaid, text, dockerfile, http, diff, ini) are
|
|
12
|
+
# always allowed; markdown is allowed too.
|
|
13
|
+
#
|
|
14
|
+
# Usage: bash .pi/scripts/check_language_consistency.sh [--strict]
|
|
15
|
+
# --strict also treat other framework languages (angular/nextjs) as foreign
|
|
16
|
+
# ============================================================================
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
|
20
|
+
PASS=0; FAIL=0; WARN=0
|
|
21
|
+
pass() { echo -e "${GREEN}✅ PASS${NC} $1"; PASS=$((PASS+1)); }
|
|
22
|
+
fail() { echo -e "${RED}❌ FAIL${NC} $1"; FAIL=$((FAIL+1)); }
|
|
23
|
+
warn() { echo -e "${YELLOW}⚠️ WARN${NC} $1"; WARN=$((WARN+1)); }
|
|
24
|
+
|
|
25
|
+
STRICT=0
|
|
26
|
+
[ "${1:-}" = "--strict" ] && STRICT=1
|
|
27
|
+
|
|
28
|
+
echo "============================================"
|
|
29
|
+
echo " Language Consistency (architecture docs)"
|
|
30
|
+
echo "============================================"
|
|
31
|
+
|
|
32
|
+
# ── Resolve project language ────────────────────────────────────────────────
|
|
33
|
+
LANG_NAME=""
|
|
34
|
+
if [ -f "guardian-manifest.json" ]; then
|
|
35
|
+
LANG_NAME=$(python3 -c "import json;print(json.load(open('guardian-manifest.json')).get('language',''))" 2>/dev/null || true)
|
|
36
|
+
fi
|
|
37
|
+
if [ -z "$LANG_NAME" ] && [ -f ".pi/guardian-manifest.json" ]; then
|
|
38
|
+
LANG_NAME=$(python3 -c "import json;print(json.load(open('.pi/guardian-manifest.json')).get('language',''))" 2>/dev/null || true)
|
|
39
|
+
fi
|
|
40
|
+
if [ -z "$LANG_NAME" ]; then
|
|
41
|
+
echo "no manifest language — skipping"
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
echo "Project language: $LANG_NAME"
|
|
45
|
+
echo ""
|
|
46
|
+
|
|
47
|
+
case "$LANG_NAME" in
|
|
48
|
+
typescript|nextjs|angular) EXPECTED="typescript|tsx|ts|javascript|jsx|js" ;;
|
|
49
|
+
python) EXPECTED="python|py" ;;
|
|
50
|
+
rust) EXPECTED="rust" ;;
|
|
51
|
+
go) EXPECTED="go" ;;
|
|
52
|
+
java) EXPECTED="java" ;;
|
|
53
|
+
*) EXPECTED="" ;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
if [ -z "$EXPECTED" ]; then
|
|
57
|
+
echo "unsupported language '$LANG_NAME' — skipping"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
ALL_FENCES="typescript|tsx|ts|javascript|jsx|js|python|py|rust|go|java|kotlin|kt|csharp|cs|ruby|rb|php|swift|cpp|c\+\+|c|scala|elixir|dart"
|
|
62
|
+
ALLOWED_NEUTRAL="json|jsonc|yaml|yml|toml|bash|sh|shell|zsh|sql|mermaid|text|txt|plaintext|dockerfile|docker|http|diff|ini|properties|xml|html|css|scss|makefile|cmake|gradle|protobuf|proto|csv|regex|"
|
|
63
|
+
|
|
64
|
+
is_expected() {
|
|
65
|
+
local f="$1"
|
|
66
|
+
[ -z "$f" ] && return 1 # bare ``` with no language
|
|
67
|
+
case "$EXPECTED" in
|
|
68
|
+
*"|$f|"*|"$f|"*) return 0 ;;
|
|
69
|
+
esac
|
|
70
|
+
# substring match for multi-alias lists
|
|
71
|
+
echo "$EXPECTED" | tr '|' '\n' | grep -qx "$f" && return 0
|
|
72
|
+
return 1
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
is_neutral() {
|
|
76
|
+
local f="$1"
|
|
77
|
+
[ -z "$f" ] && return 0
|
|
78
|
+
echo "$ALLOWED_NEUTRAL" | tr '|' '\n' | grep -qx "$f"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ROOT=".pi/architecture"
|
|
82
|
+
[ -d "$ROOT" ] || { echo "no $ROOT — nothing to check"; exit 0; }
|
|
83
|
+
|
|
84
|
+
FILES=$(find "$ROOT" -name "*.md" -type f 2>/dev/null | grep -v "/modules/module-template.md$")
|
|
85
|
+
MISMATCHES=0
|
|
86
|
+
CHECKED=0
|
|
87
|
+
for doc in $FILES; do
|
|
88
|
+
CHECKED=$((CHECKED+1))
|
|
89
|
+
# Collect fenced block languages
|
|
90
|
+
fences=$(grep -oE '^```[a-zA-Z0-9_+-]*' "$doc" 2>/dev/null | sed 's/^```//' | sort -u)
|
|
91
|
+
[ -z "$fences" ] && continue
|
|
92
|
+
bad=""
|
|
93
|
+
for f in $fences; do
|
|
94
|
+
lower=$(echo "$f" | tr '[:upper:]' '[:lower:]')
|
|
95
|
+
if is_neutral "$lower"; then continue; fi
|
|
96
|
+
if is_expected "$lower"; then continue; fi
|
|
97
|
+
# A fence that is a known programming language but not this project's
|
|
98
|
+
if echo "$lower" | grep -qE "^($ALL_FENCES)$"; then
|
|
99
|
+
bad="$bad $lower"
|
|
100
|
+
elif [ "$STRICT" = "1" ]; then
|
|
101
|
+
bad="$bad $lower"
|
|
102
|
+
fi
|
|
103
|
+
done
|
|
104
|
+
if [ -n "$bad" ]; then
|
|
105
|
+
fail "$doc uses foreign code fence(s):$(echo "$bad" | tr ' ' '\n' | sed '/^$/d' | sort -u | sed 's/^/ /' | tr '\n' ',' | sed 's/,$//') (project: $LANG_NAME)"
|
|
106
|
+
MISMATCHES=$((MISMATCHES+1))
|
|
107
|
+
fi
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
if [ "$MISMATCHES" -eq 0 ]; then
|
|
111
|
+
pass "$CHECKED architecture doc(s) use $LANG_NAME (no foreign code fences)"
|
|
112
|
+
else
|
|
113
|
+
fail "$MISMATCHES of $CHECKED architecture doc(s) contain foreign-language code samples"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
echo ""
|
|
117
|
+
echo "============================================"
|
|
118
|
+
echo " Summary"
|
|
119
|
+
echo "============================================"
|
|
120
|
+
echo " Passed: $PASS Failed: $FAIL Warnings: $WARN"
|
|
121
|
+
[ "$FAIL" -eq 0 ] && exit 0
|
|
122
|
+
exit 1
|
|
@@ -13,10 +13,28 @@ detect_language() {
|
|
|
13
13
|
|
|
14
14
|
LANG=$(detect_language)
|
|
15
15
|
LANG_SCRIPT="${SCRIPT_DIR}/languages/${LANG}/validate-architecture.sh"
|
|
16
|
+
CONSISTENCY_SCRIPT="${SCRIPT_DIR}/check_language_consistency.sh"
|
|
17
|
+
|
|
18
|
+
# Language consistency runs for EVERY language (architecture docs must use the
|
|
19
|
+
# project language — catches scaffold stubs emitted in the wrong language).
|
|
20
|
+
run_consistency() {
|
|
21
|
+
if [ -f "$CONSISTENCY_SCRIPT" ]; then
|
|
22
|
+
bash "$CONSISTENCY_SCRIPT" || CONSISTENCY_RC=1
|
|
23
|
+
echo ""
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
16
26
|
|
|
17
27
|
if [ -f "$LANG_SCRIPT" ]; then
|
|
18
|
-
|
|
28
|
+
bash "$LANG_SCRIPT" "$@"
|
|
29
|
+
LANG_RC=$?
|
|
30
|
+
CONSISTENCY_RC=0
|
|
31
|
+
run_consistency
|
|
32
|
+
[ "$LANG_RC" -ne 0 ] && exit "$LANG_RC"
|
|
33
|
+
[ "${CONSISTENCY_RC:-0}" -ne 0 ] && exit 1
|
|
34
|
+
exit 0
|
|
19
35
|
else
|
|
20
36
|
echo "No architecture validator for language: $LANG"
|
|
21
|
-
|
|
37
|
+
CONSISTENCY_RC=0
|
|
38
|
+
run_consistency
|
|
39
|
+
exit "${CONSISTENCY_RC:-0}"
|
|
22
40
|
fi
|