avorelo 0.1.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/LICENSE +21 -0
- package/README.md +56 -0
- package/bin/avorelo +9 -0
- package/package.json +135 -0
- package/scripts/README.md +40 -0
- package/scripts/cco-dashboard.js +252 -0
- package/scripts/cco-status.js +430 -0
- package/scripts/lib/activation/account-state.js +37 -0
- package/scripts/lib/activation/activation-runner.js +546 -0
- package/scripts/lib/activation/activation-self-healing.js +480 -0
- package/scripts/lib/activation/activation-state.js +83 -0
- package/scripts/lib/activation/activation-summary.js +191 -0
- package/scripts/lib/activation/adapters/claude-code.js +77 -0
- package/scripts/lib/activation/adapters/codex-cli.js +52 -0
- package/scripts/lib/activation/adapters/cursor.js +37 -0
- package/scripts/lib/activation/adapters/github-agent.js +39 -0
- package/scripts/lib/activation/adapters/terminal.js +42 -0
- package/scripts/lib/activation/adapters/vscode.js +39 -0
- package/scripts/lib/activation/adapters/windsurf.js +37 -0
- package/scripts/lib/activation/ai-surface-detector.js +151 -0
- package/scripts/lib/activation/connect-account.js +145 -0
- package/scripts/lib/activation/detect-environment.js +75 -0
- package/scripts/lib/activation/detect-hosts.js +62 -0
- package/scripts/lib/activation/format-activation-output.js +109 -0
- package/scripts/lib/activation/next-action.js +43 -0
- package/scripts/lib/activation/repair-engine.js +219 -0
- package/scripts/lib/activation-distribution-readiness.js +507 -0
- package/scripts/lib/adapter-conformance.js +176 -0
- package/scripts/lib/adapter-readiness.js +417 -0
- package/scripts/lib/adapter-safety-boundaries.js +335 -0
- package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
- package/scripts/lib/agent-access-governance.js +455 -0
- package/scripts/lib/agent-enforcement.js +765 -0
- package/scripts/lib/agent-policy-profile.js +210 -0
- package/scripts/lib/agent-security/action-evaluator.js +507 -0
- package/scripts/lib/agent-security/adapter-registry.js +98 -0
- package/scripts/lib/agent-security/auto-policy.js +139 -0
- package/scripts/lib/agent-security/bounded-scan.js +93 -0
- package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
- package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
- package/scripts/lib/agent-security/file-write-adapter.js +183 -0
- package/scripts/lib/agent-security/file-write-rules.js +178 -0
- package/scripts/lib/agent-security/index.js +3342 -0
- package/scripts/lib/agent-security/instruction-risk.js +181 -0
- package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
- package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
- package/scripts/lib/agent-security/package-action-adapter.js +175 -0
- package/scripts/lib/agent-security/package-action-rules.js +233 -0
- package/scripts/lib/agent-security/performance.js +148 -0
- package/scripts/lib/agent-security/permission-minimizer.js +403 -0
- package/scripts/lib/agent-security/scan-cache.js +74 -0
- package/scripts/lib/agent-security/source-trust.js +146 -0
- package/scripts/lib/ai-install-prompt.js +288 -0
- package/scripts/lib/ai-workspace-hygiene.js +1499 -0
- package/scripts/lib/alpha-activation.js +520 -0
- package/scripts/lib/alpha-feedback.js +263 -0
- package/scripts/lib/alpha-readiness-gate.js +332 -0
- package/scripts/lib/anti-gaming.js +169 -0
- package/scripts/lib/artifact-health.js +431 -0
- package/scripts/lib/attribution.js +180 -0
- package/scripts/lib/audit.js +289 -0
- package/scripts/lib/avorelo-skill-registry.js +810 -0
- package/scripts/lib/batch-jobs.js +71 -0
- package/scripts/lib/brain-pack.js +578 -0
- package/scripts/lib/brand-boundary.js +424 -0
- package/scripts/lib/brand.js +74 -0
- package/scripts/lib/browser-capability.js +1048 -0
- package/scripts/lib/browser-proof-preflight.js +321 -0
- package/scripts/lib/cache-readiness.js +187 -0
- package/scripts/lib/canonical-reentry.js +162 -0
- package/scripts/lib/capability-packs.js +314 -0
- package/scripts/lib/capability-recommender.js +512 -0
- package/scripts/lib/capability-registry.js +1059 -0
- package/scripts/lib/carry-forward-surfacing.js +194 -0
- package/scripts/lib/ccusage-adapter.js +188 -0
- package/scripts/lib/company-loop.js +1149 -0
- package/scripts/lib/config.js +637 -0
- package/scripts/lib/context-acquisition-plan.js +287 -0
- package/scripts/lib/context-budget-guard.js +170 -0
- package/scripts/lib/context-budget-scanner.js +257 -0
- package/scripts/lib/context-optimizer.js +715 -0
- package/scripts/lib/context-reduction-plan.js +178 -0
- package/scripts/lib/context-safety.js +88 -0
- package/scripts/lib/context-savings-engine.js +158 -0
- package/scripts/lib/cost-evidence.js +254 -0
- package/scripts/lib/cross-host-install-plan.js +308 -0
- package/scripts/lib/cross-host-install-readiness.js +237 -0
- package/scripts/lib/cross-host-value-flow.js +268 -0
- package/scripts/lib/dashboard.js +900 -0
- package/scripts/lib/design-partner-feedback.js +346 -0
- package/scripts/lib/entitlements.js +100 -0
- package/scripts/lib/execution-packet.js +559 -0
- package/scripts/lib/experimentation-events.js +547 -0
- package/scripts/lib/external-capability-compliance.js +107 -0
- package/scripts/lib/external-user-simulation.js +166 -0
- package/scripts/lib/failure-recovery-readiness.js +81 -0
- package/scripts/lib/failure-recovery.js +419 -0
- package/scripts/lib/feedback-intelligence.js +537 -0
- package/scripts/lib/feedback-signals.js +205 -0
- package/scripts/lib/file-integrity.js +68 -0
- package/scripts/lib/fsx.js +127 -0
- package/scripts/lib/full-readiness-gate.js +451 -0
- package/scripts/lib/guidance-builder.js +174 -0
- package/scripts/lib/hook-apply.js +1019 -0
- package/scripts/lib/hook-baseline.js +310 -0
- package/scripts/lib/hook-config-preview.js +275 -0
- package/scripts/lib/hook-contracts.js +290 -0
- package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
- package/scripts/lib/host-capability-matrix.js +351 -0
- package/scripts/lib/host-support-context.js +254 -0
- package/scripts/lib/http-hook-action.js +538 -0
- package/scripts/lib/install-ai-readiness.js +84 -0
- package/scripts/lib/install-intake-risk.js +1037 -0
- package/scripts/lib/install-journey-intelligence.js +329 -0
- package/scripts/lib/intervention-guidance.js +57 -0
- package/scripts/lib/known-limitations.js +115 -0
- package/scripts/lib/l8-path-truth.js +146 -0
- package/scripts/lib/launch-hardening-gate.js +436 -0
- package/scripts/lib/launch-readiness.js +628 -0
- package/scripts/lib/learning-memory.js +686 -0
- package/scripts/lib/lifecycle-hooks.js +802 -0
- package/scripts/lib/local-package-smoke.js +423 -0
- package/scripts/lib/local-pricing.js +299 -0
- package/scripts/lib/mcp-enforcement.js +311 -0
- package/scripts/lib/mcp-least-privilege-policy.js +303 -0
- package/scripts/lib/mcp-tool-inventory.js +388 -0
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +335 -0
- package/scripts/lib/metrics.js +699 -0
- package/scripts/lib/micro-proof.js +133 -0
- package/scripts/lib/next-run-context.js +436 -0
- package/scripts/lib/operating-value.js +1648 -0
- package/scripts/lib/optimization-v3.js +122 -0
- package/scripts/lib/orchestration/adapters/_shared.js +49 -0
- package/scripts/lib/orchestration/adapters/aider.js +18 -0
- package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
- package/scripts/lib/orchestration/adapters/codex.js +35 -0
- package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
- package/scripts/lib/orchestration/adapters/git.js +25 -0
- package/scripts/lib/orchestration/adapters/index.js +31 -0
- package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
- package/scripts/lib/orchestration/adapters/ollama.js +18 -0
- package/scripts/lib/orchestration/adapters/opencode.js +18 -0
- package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
- package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
- package/scripts/lib/orchestration/cli.js +438 -0
- package/scripts/lib/orchestration/execution-manager.js +279 -0
- package/scripts/lib/orchestration/handoff.js +314 -0
- package/scripts/lib/orchestration/index.js +456 -0
- package/scripts/lib/orchestration/inventory.js +47 -0
- package/scripts/lib/orchestration/model-discovery.js +498 -0
- package/scripts/lib/orchestration/model-profiler.js +170 -0
- package/scripts/lib/orchestration/model-profiles.js +252 -0
- package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
- package/scripts/lib/orchestration/proof-writer.js +349 -0
- package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
- package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
- package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
- package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
- package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
- package/scripts/lib/orchestration/risk-classifier.js +130 -0
- package/scripts/lib/orchestration/routing-policy.js +486 -0
- package/scripts/lib/orchestration/settings.js +112 -0
- package/scripts/lib/orchestration/state.js +165 -0
- package/scripts/lib/orchestration/verification-manager.js +138 -0
- package/scripts/lib/output-profiles.js +146 -0
- package/scripts/lib/package-content-audit.js +368 -0
- package/scripts/lib/package-runtime.js +278 -0
- package/scripts/lib/plan-surface.js +53 -0
- package/scripts/lib/plans.js +2318 -0
- package/scripts/lib/policy-provider.js +27 -0
- package/scripts/lib/prelaunch-activation-readiness.js +409 -0
- package/scripts/lib/prelaunch-evidence-store.js +816 -0
- package/scripts/lib/prelaunch-intelligence.js +869 -0
- package/scripts/lib/pricing-experiment.js +118 -0
- package/scripts/lib/pro-moment-events.js +77 -0
- package/scripts/lib/pro-moment-state.js +227 -0
- package/scripts/lib/pro-moments.js +1216 -0
- package/scripts/lib/product-learning-events.js +629 -0
- package/scripts/lib/project-profile.js +555 -0
- package/scripts/lib/prompt-compiler.js +280 -0
- package/scripts/lib/prompt-lint.js +32 -0
- package/scripts/lib/prompt-suggestions.js +52 -0
- package/scripts/lib/proof-canonical.js +398 -0
- package/scripts/lib/proof-drilldown.js +383 -0
- package/scripts/lib/proof-events.js +342 -0
- package/scripts/lib/proof-history.js +243 -0
- package/scripts/lib/proof-metrics.js +296 -0
- package/scripts/lib/proof-outcome-evidence.js +134 -0
- package/scripts/lib/proof-receipt.js +335 -0
- package/scripts/lib/proof-record.js +461 -0
- package/scripts/lib/public-activation-distribution-gate.js +258 -0
- package/scripts/lib/public-cli.js +3891 -0
- package/scripts/lib/public-distribution-truth.js +211 -0
- package/scripts/lib/public-install-claim-checker.js +294 -0
- package/scripts/lib/publish-provenance-readiness.js +283 -0
- package/scripts/lib/readiness-delta.js +218 -0
- package/scripts/lib/readiness-evidence-closure.js +196 -0
- package/scripts/lib/reentry-memory-capture.js +241 -0
- package/scripts/lib/reentry-memory-retrieval.js +302 -0
- package/scripts/lib/reentry-memory-status.js +146 -0
- package/scripts/lib/reentry-memory-store.js +178 -0
- package/scripts/lib/reentry-state.js +66 -0
- package/scripts/lib/release-candidate-bundle.js +166 -0
- package/scripts/lib/remediation.js +81 -0
- package/scripts/lib/repo-map.js +391 -0
- package/scripts/lib/run-improvements-lifecycle.js +330 -0
- package/scripts/lib/run-improvements.js +789 -0
- package/scripts/lib/runtime-decision-policy.js +387 -0
- package/scripts/lib/safe-path-engine.js +705 -0
- package/scripts/lib/safe-run-controller.js +887 -0
- package/scripts/lib/score.js +262 -0
- package/scripts/lib/seamless-enforcement.js +329 -0
- package/scripts/lib/seamless-outcome.js +689 -0
- package/scripts/lib/seamless-reality-gate.js +5043 -0
- package/scripts/lib/security-risk-classifier.js +511 -0
- package/scripts/lib/security-scan.js +384 -0
- package/scripts/lib/session-context-optimizer.js +1211 -0
- package/scripts/lib/session-timing.js +315 -0
- package/scripts/lib/skill-hygiene.js +805 -0
- package/scripts/lib/skill-packs.js +161 -0
- package/scripts/lib/skills-operating-layer.js +580 -0
- package/scripts/lib/smart-work-routing.js +768 -0
- package/scripts/lib/source-catalog.js +700 -0
- package/scripts/lib/status-value-summary.js +32 -0
- package/scripts/lib/support-bundle.js +578 -0
- package/scripts/lib/task-continuation.js +440 -0
- package/scripts/lib/test-helpers.js +15 -0
- package/scripts/lib/tier.js +38 -0
- package/scripts/lib/token-context-quality-gate.js +370 -0
- package/scripts/lib/token-cost-capture.js +187 -0
- package/scripts/lib/token-cost-intelligence.js +358 -0
- package/scripts/lib/token-efficiency-evidence.js +213 -0
- package/scripts/lib/token-evidence.js +699 -0
- package/scripts/lib/tokenish.js +17 -0
- package/scripts/lib/tool-output-sandbox.js +304 -0
- package/scripts/lib/trust-audit.js +136 -0
- package/scripts/lib/unified-events.js +396 -0
- package/scripts/lib/upgrade-interruption-recovery.js +407 -0
- package/scripts/lib/usage-ledger.js +201 -0
- package/scripts/lib/value-ledger.js +130 -0
- package/scripts/lib/value-proof-calibration.js +531 -0
- package/scripts/lib/visual-qa.js +231 -0
- package/scripts/lib/voice-alpha.js +29 -0
- package/scripts/lib/work-aware-orchestration.js +976 -0
- package/scripts/lib/work-control-receipts.js +577 -0
- package/scripts/lib/work-ledger.js +1123 -0
- package/scripts/lib/work-panel-preview.js +352 -0
- package/scripts/lib/workflow-discipline.js +280 -0
- package/scripts/lib/workflow-signals.js +419 -0
- package/scripts/lib/workspace-map.js +281 -0
- package/scripts/lib/workspace-registry.js +1367 -0
- package/scripts/lib/workspace-resolver.js +480 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const brand = require("./brand");
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CONFIG_PATH = path.join(__dirname, "..", "brand-boundary.config.json");
|
|
8
|
+
const TEXT_EXTENSIONS = new Set([
|
|
9
|
+
".js",
|
|
10
|
+
".cjs",
|
|
11
|
+
".mjs",
|
|
12
|
+
".ts",
|
|
13
|
+
".tsx",
|
|
14
|
+
".jsx",
|
|
15
|
+
".json",
|
|
16
|
+
".md",
|
|
17
|
+
".html",
|
|
18
|
+
".css",
|
|
19
|
+
".txt",
|
|
20
|
+
".yml",
|
|
21
|
+
".yaml",
|
|
22
|
+
]);
|
|
23
|
+
const CONDITIONAL_TEXT_EXTENSIONS = new Set([".md", ".txt", ".html", ".htm"]);
|
|
24
|
+
|
|
25
|
+
function normalizeSlashes(value) {
|
|
26
|
+
return String(value || "").replace(/\\/g, "/");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function escapeRegExp(value) {
|
|
30
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadBrandBoundaryConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const wrapped = new Error(`Could not load brand boundary config at ${configPath}: ${error.message}`);
|
|
38
|
+
wrapped.code = "BRAND_CONFIG_ERROR";
|
|
39
|
+
throw wrapped;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveReplacementValue(token) {
|
|
44
|
+
return brand[token] || token;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function matchesPathPrefix(relPath, candidate) {
|
|
48
|
+
const normalizedPath = normalizeSlashes(relPath);
|
|
49
|
+
const normalizedCandidate = normalizeSlashes(candidate).replace(/\/+$/, "");
|
|
50
|
+
return normalizedPath === normalizedCandidate || normalizedPath.startsWith(`${normalizedCandidate}/`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isIgnoredPath(relPath, config) {
|
|
54
|
+
return (config.ignoredPaths || []).some((candidate) => matchesPathPrefix(relPath, candidate));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isTextFile(relPath) {
|
|
58
|
+
return TEXT_EXTENSIONS.has(path.extname(relPath).toLowerCase());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function shouldCheckConditionalTerm(relPath) {
|
|
62
|
+
return CONDITIONAL_TEXT_EXTENSIONS.has(path.extname(relPath).toLowerCase());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function collectPath(rootDir, relPath, config, bucket) {
|
|
66
|
+
const absPath = path.join(rootDir, relPath);
|
|
67
|
+
if (!fs.existsSync(absPath)) return;
|
|
68
|
+
const stat = fs.statSync(absPath);
|
|
69
|
+
|
|
70
|
+
if (isIgnoredPath(relPath, config)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (stat.isDirectory()) {
|
|
75
|
+
fs.readdirSync(absPath)
|
|
76
|
+
.sort()
|
|
77
|
+
.forEach((entry) => collectPath(rootDir, path.posix.join(normalizeSlashes(relPath), entry), config, bucket));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (stat.isFile() && isTextFile(relPath)) {
|
|
82
|
+
bucket.push({
|
|
83
|
+
relPath: normalizeSlashes(relPath),
|
|
84
|
+
absPath,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function resolveCustomerFacingFiles(rootDir, config) {
|
|
90
|
+
const files = [];
|
|
91
|
+
(config.customerFacingPaths || []).forEach((entry) => {
|
|
92
|
+
collectPath(rootDir, normalizeSlashes(entry), config, files);
|
|
93
|
+
});
|
|
94
|
+
const deduped = new Map();
|
|
95
|
+
files.forEach((file) => {
|
|
96
|
+
deduped.set(file.relPath, file);
|
|
97
|
+
});
|
|
98
|
+
return Array.from(deduped.values()).sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isAllowlisted(relPath, term, config) {
|
|
102
|
+
return (config.allowedLegacyOccurrences || []).find((entry) => {
|
|
103
|
+
return normalizeSlashes(entry.path) === normalizeSlashes(relPath) && entry.term === term;
|
|
104
|
+
}) || null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function hasAllowlistedFile(relPath, config) {
|
|
108
|
+
return (config.allowedLegacyOccurrences || []).some((entry) => {
|
|
109
|
+
return normalizeSlashes(entry.path) === normalizeSlashes(relPath);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function buildSuggestionMap(config) {
|
|
114
|
+
const map = new Map();
|
|
115
|
+
(config.replacementRules || []).forEach((rule) => {
|
|
116
|
+
map.set(rule.term, resolveReplacementValue(rule.replaceWith));
|
|
117
|
+
});
|
|
118
|
+
map.set("getwuz.com", brand.PUBLIC_DOMAIN);
|
|
119
|
+
map.set("usewuz.com", brand.PUBLIC_DOMAIN);
|
|
120
|
+
map.set("wuz.ai", brand.PUBLIC_DOMAIN);
|
|
121
|
+
map.set("wuz.dev", brand.PUBLIC_DOMAIN);
|
|
122
|
+
const supportEmail = config.supportEmail || brand.SUPPORT_EMAIL;
|
|
123
|
+
if (supportEmail) {
|
|
124
|
+
map.set("SUPPORT_EMAIL", supportEmail);
|
|
125
|
+
}
|
|
126
|
+
return map;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function addMatchesFromRegex(leaks, { relPath, lines, term, regex, config, suggestionMap, reason }) {
|
|
130
|
+
lines.forEach((line, index) => {
|
|
131
|
+
regex.lastIndex = 0;
|
|
132
|
+
if (!regex.test(line)) return;
|
|
133
|
+
if (isAllowlisted(relPath, term, config)) return;
|
|
134
|
+
leaks.push({
|
|
135
|
+
path: relPath,
|
|
136
|
+
line: index + 1,
|
|
137
|
+
term,
|
|
138
|
+
excerpt: line.trim(),
|
|
139
|
+
suggestedReplacement: suggestionMap.get(term) || null,
|
|
140
|
+
reason,
|
|
141
|
+
allowlistReason: null,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function buildForbiddenRegex(term) {
|
|
147
|
+
if (String(term).startsWith("/")) {
|
|
148
|
+
return new RegExp(`(?<!\\.)${escapeRegExp(term)}\\b`, "g");
|
|
149
|
+
}
|
|
150
|
+
if (term === "cco-init") {
|
|
151
|
+
return /\bcco-init\b/g;
|
|
152
|
+
}
|
|
153
|
+
return new RegExp(escapeRegExp(term), "g");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function scanText({ relPath, text, config }) {
|
|
157
|
+
const lines = String(text).split(/\r?\n/);
|
|
158
|
+
const suggestionMap = buildSuggestionMap(config);
|
|
159
|
+
const leaks = [];
|
|
160
|
+
|
|
161
|
+
(config.forbiddenTerms || []).forEach((term) => {
|
|
162
|
+
if (term === "wuz" && !shouldCheckConditionalTerm(relPath)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const regex = buildForbiddenRegex(term);
|
|
166
|
+
addMatchesFromRegex(leaks, {
|
|
167
|
+
relPath,
|
|
168
|
+
lines,
|
|
169
|
+
term,
|
|
170
|
+
regex,
|
|
171
|
+
config,
|
|
172
|
+
suggestionMap,
|
|
173
|
+
reason: "customer-facing surfaces must use the Avorelo public brand.",
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
(config.forbiddenDomains || []).forEach((term) => {
|
|
178
|
+
const regex = new RegExp(escapeRegExp(term), "g");
|
|
179
|
+
addMatchesFromRegex(leaks, {
|
|
180
|
+
relPath,
|
|
181
|
+
lines,
|
|
182
|
+
term,
|
|
183
|
+
regex,
|
|
184
|
+
config,
|
|
185
|
+
suggestionMap,
|
|
186
|
+
reason: "customer-facing surfaces must use avorelo.com where the public domain is expected.",
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (shouldCheckConditionalTerm(relPath)) {
|
|
191
|
+
(config.conditionalForbiddenTerms || [])
|
|
192
|
+
.filter((entry) => entry.forbiddenWhenCustomerFacing)
|
|
193
|
+
.forEach((entry) => {
|
|
194
|
+
const term = entry.term;
|
|
195
|
+
const regex = new RegExp(`\\b${escapeRegExp(term)}\\b`, "g");
|
|
196
|
+
addMatchesFromRegex(leaks, {
|
|
197
|
+
relPath,
|
|
198
|
+
lines,
|
|
199
|
+
term,
|
|
200
|
+
regex,
|
|
201
|
+
config,
|
|
202
|
+
suggestionMap,
|
|
203
|
+
reason: "customer-facing lowercase cco is ambiguous and must be removed or allowlisted with a reason.",
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const supportEmail = String(config.supportEmail || brand.SUPPORT_EMAIL || "").trim().toLowerCase();
|
|
209
|
+
const supportAddressRegex = /\b(?:support|contact|hello|info)@[a-z0-9.-]+\.[a-z]{2,}\b/gi;
|
|
210
|
+
lines.forEach((line, index) => {
|
|
211
|
+
const matches = line.match(supportAddressRegex) || [];
|
|
212
|
+
matches.forEach((match) => {
|
|
213
|
+
const normalized = String(match).toLowerCase();
|
|
214
|
+
if (normalized === supportEmail) return;
|
|
215
|
+
if (isAllowlisted(relPath, match, config)) return;
|
|
216
|
+
leaks.push({
|
|
217
|
+
path: relPath,
|
|
218
|
+
line: index + 1,
|
|
219
|
+
term: match,
|
|
220
|
+
excerpt: line.trim(),
|
|
221
|
+
suggestedReplacement: supportEmail || null,
|
|
222
|
+
reason: "customer-facing support and contact surfaces must use support@avorelo.com.",
|
|
223
|
+
allowlistReason: null,
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return leaks;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function scanCustomerFacingFiles(rootDir, config = loadBrandBoundaryConfig()) {
|
|
232
|
+
const files = resolveCustomerFacingFiles(rootDir, config);
|
|
233
|
+
const leaks = [];
|
|
234
|
+
|
|
235
|
+
files.forEach((file) => {
|
|
236
|
+
const text = fs.readFileSync(file.absPath, "utf8");
|
|
237
|
+
leaks.push(...scanText({ relPath: file.relPath, text, config }));
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
files,
|
|
242
|
+
leaks,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function formatLeak(leak) {
|
|
247
|
+
const suggested = leak.suggestedReplacement ? `Suggested replacement: ${leak.suggestedReplacement}\n` : "";
|
|
248
|
+
return [
|
|
249
|
+
"Forbidden legacy brand found:",
|
|
250
|
+
`File: ${leak.path}`,
|
|
251
|
+
`Line: ${leak.line}`,
|
|
252
|
+
`Term: ${leak.term}`,
|
|
253
|
+
`Excerpt: ${leak.excerpt}`,
|
|
254
|
+
suggested.trimEnd(),
|
|
255
|
+
`Reason: ${leak.reason}`,
|
|
256
|
+
"Action: run npm run brand:fix or edit manually.",
|
|
257
|
+
].filter(Boolean).join("\n");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function applyLiteralReplacement(content, term, replacement) {
|
|
261
|
+
const regex = new RegExp(escapeRegExp(term), "g");
|
|
262
|
+
const matches = content.match(regex);
|
|
263
|
+
if (!matches) {
|
|
264
|
+
return { content, count: 0 };
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
content: content.replace(regex, replacement),
|
|
268
|
+
count: matches.length,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function applySafeLowercaseWuz(content) {
|
|
273
|
+
const patterns = [
|
|
274
|
+
{ regex: /\bnpx wuz@latest\b/g, replacement: "npx avorelo@latest", term: "wuz" },
|
|
275
|
+
{ regex: /\bUsage: wuz\b/g, replacement: "Usage: avorelo", term: "wuz" },
|
|
276
|
+
{ regex: /\[wuz\]/g, replacement: "[avorelo]", term: "wuz" },
|
|
277
|
+
{ regex: /\bwuz verify\b/g, replacement: "avorelo verify", term: "wuz" },
|
|
278
|
+
{ regex: /^wuz$/gm, replacement: "avorelo", term: "wuz" },
|
|
279
|
+
{ regex: /\bwuz 1\.[0-9.]+\b/g, replacement: (match) => match.replace(/\bwuz\b/, "avorelo"), term: "wuz" },
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
let next = content;
|
|
283
|
+
let count = 0;
|
|
284
|
+
patterns.forEach(({ regex, replacement }) => {
|
|
285
|
+
const matches = next.match(regex);
|
|
286
|
+
if (!matches) return;
|
|
287
|
+
next = next.replace(regex, replacement);
|
|
288
|
+
count += matches.length;
|
|
289
|
+
});
|
|
290
|
+
return { content: next, count };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function findAmbiguousTerms(relPath, text, config) {
|
|
294
|
+
const ambiguous = [];
|
|
295
|
+
const lines = String(text).split(/\r?\n/);
|
|
296
|
+
|
|
297
|
+
if (shouldCheckConditionalTerm(relPath)) {
|
|
298
|
+
lines.forEach((line, index) => {
|
|
299
|
+
if (/\bcco\b/.test(line) && !isAllowlisted(relPath, "cco", config)) {
|
|
300
|
+
ambiguous.push({
|
|
301
|
+
path: relPath,
|
|
302
|
+
line: index + 1,
|
|
303
|
+
term: "cco",
|
|
304
|
+
excerpt: line.trim(),
|
|
305
|
+
reason: "customer-facing lowercase cco should be replaced manually or allowlisted if intentionally internal.",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (shouldCheckConditionalTerm(relPath)) {
|
|
312
|
+
lines.forEach((line, index) => {
|
|
313
|
+
if (/\bwuz\b/.test(line) && !/npx wuz@latest|Usage: wuz|\[wuz\]|\bwuz verify\b|^wuz$|\bwuz 1\.[0-9.]+\b/.test(line) && !isAllowlisted(relPath, "wuz", config)) {
|
|
314
|
+
ambiguous.push({
|
|
315
|
+
path: relPath,
|
|
316
|
+
line: index + 1,
|
|
317
|
+
term: "wuz",
|
|
318
|
+
excerpt: line.trim(),
|
|
319
|
+
reason: "lowercase wuz was not auto-fixed because the replacement depends on context.",
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return ambiguous;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function applyReplacementRules(relPath, input, config) {
|
|
329
|
+
let content = String(input);
|
|
330
|
+
const replacementCounts = {};
|
|
331
|
+
const ambiguous = [];
|
|
332
|
+
|
|
333
|
+
(config.replacementRules || [])
|
|
334
|
+
.filter((rule) => !rule.onlyWhenSafe && rule.term !== "wuz")
|
|
335
|
+
.forEach((rule) => {
|
|
336
|
+
const replacement = resolveReplacementValue(rule.replaceWith);
|
|
337
|
+
const result = applyLiteralReplacement(content, rule.term, replacement);
|
|
338
|
+
content = result.content;
|
|
339
|
+
if (result.count > 0) {
|
|
340
|
+
replacementCounts[rule.term] = (replacementCounts[rule.term] || 0) + result.count;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
["getwuz.com", "usewuz.com", "wuz.ai", "wuz.dev"].forEach((term) => {
|
|
345
|
+
const result = applyLiteralReplacement(content, term, brand.PUBLIC_DOMAIN);
|
|
346
|
+
content = result.content;
|
|
347
|
+
if (result.count > 0) {
|
|
348
|
+
replacementCounts[term] = (replacementCounts[term] || 0) + result.count;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const safeWuz = applySafeLowercaseWuz(content);
|
|
353
|
+
content = safeWuz.content;
|
|
354
|
+
if (safeWuz.count > 0) {
|
|
355
|
+
replacementCounts.wuz = (replacementCounts.wuz || 0) + safeWuz.count;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const supportEmail = String(config.supportEmail || brand.SUPPORT_EMAIL || "").trim();
|
|
359
|
+
if (supportEmail) {
|
|
360
|
+
const supportAddressRegex = /\b(?:support|contact|hello|info)@[a-z0-9.-]+\.[a-z]{2,}\b/gi;
|
|
361
|
+
const matches = content.match(supportAddressRegex) || [];
|
|
362
|
+
const wrongMatches = matches.filter((match) => String(match).toLowerCase() !== supportEmail.toLowerCase());
|
|
363
|
+
if (wrongMatches.length > 0) {
|
|
364
|
+
content = content.replace(supportAddressRegex, (match) => {
|
|
365
|
+
return String(match).toLowerCase() === supportEmail.toLowerCase() ? match : supportEmail;
|
|
366
|
+
});
|
|
367
|
+
replacementCounts.supportEmail = (replacementCounts.supportEmail || 0) + wrongMatches.length;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
ambiguous.push(...findAmbiguousTerms(relPath, content, config));
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
content,
|
|
375
|
+
changed: content !== input,
|
|
376
|
+
replacementCounts,
|
|
377
|
+
ambiguous,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function runAutofix(rootDir, config = loadBrandBoundaryConfig(), options = {}) {
|
|
382
|
+
const files = resolveCustomerFacingFiles(rootDir, config);
|
|
383
|
+
const write = options.write === true;
|
|
384
|
+
const changedFiles = [];
|
|
385
|
+
const ambiguous = [];
|
|
386
|
+
const replacementCounts = {};
|
|
387
|
+
|
|
388
|
+
files.forEach((file) => {
|
|
389
|
+
const before = fs.readFileSync(file.absPath, "utf8");
|
|
390
|
+
if (hasAllowlistedFile(file.relPath, config)) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const result = applyReplacementRules(file.relPath, before, config);
|
|
394
|
+
|
|
395
|
+
Object.entries(result.replacementCounts).forEach(([term, count]) => {
|
|
396
|
+
replacementCounts[term] = (replacementCounts[term] || 0) + count;
|
|
397
|
+
});
|
|
398
|
+
ambiguous.push(...result.ambiguous);
|
|
399
|
+
|
|
400
|
+
if (write && result.changed) {
|
|
401
|
+
fs.writeFileSync(file.absPath, result.content, "utf8");
|
|
402
|
+
changedFiles.push(file.relPath);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
filesScanned: files.length,
|
|
408
|
+
changedFiles,
|
|
409
|
+
replacementCounts,
|
|
410
|
+
ambiguous,
|
|
411
|
+
write,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
module.exports = {
|
|
416
|
+
DEFAULT_CONFIG_PATH,
|
|
417
|
+
loadBrandBoundaryConfig,
|
|
418
|
+
resolveCustomerFacingFiles,
|
|
419
|
+
scanText,
|
|
420
|
+
scanCustomerFacingFiles,
|
|
421
|
+
formatLeak,
|
|
422
|
+
runAutofix,
|
|
423
|
+
applyReplacementRules,
|
|
424
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const PRODUCT_NAME = "Avorelo";
|
|
4
|
+
const CLI_NAME = "avorelo";
|
|
5
|
+
const PUBLIC_INIT_COMMAND = "/avorelo-init";
|
|
6
|
+
const PUBLIC_DOMAIN = "avorelo.com";
|
|
7
|
+
const PUBLIC_SITE_URL = "https://avorelo.com";
|
|
8
|
+
const SUPPORT_EMAIL = "support@avorelo.com";
|
|
9
|
+
|
|
10
|
+
const LEGACY_PRODUCT_NAME = "Wuz";
|
|
11
|
+
const LEGACY_CLI_NAME = "wuz";
|
|
12
|
+
const LEGACY_FULL_NAME = "ClaudeCode Optimizer";
|
|
13
|
+
const LEGACY_PACKAGE_NAME = "ClaudeCode-Optimizer";
|
|
14
|
+
|
|
15
|
+
const INTERNAL_NAMESPACE = "cco";
|
|
16
|
+
const INTERNAL_INIT_COMMAND = "/cco-init";
|
|
17
|
+
|
|
18
|
+
function getBrand() {
|
|
19
|
+
return {
|
|
20
|
+
PRODUCT_NAME,
|
|
21
|
+
CLI_NAME,
|
|
22
|
+
PUBLIC_INIT_COMMAND,
|
|
23
|
+
PUBLIC_DOMAIN,
|
|
24
|
+
PUBLIC_SITE_URL,
|
|
25
|
+
SUPPORT_EMAIL,
|
|
26
|
+
LEGACY_PRODUCT_NAME,
|
|
27
|
+
LEGACY_CLI_NAME,
|
|
28
|
+
LEGACY_FULL_NAME,
|
|
29
|
+
LEGACY_PACKAGE_NAME,
|
|
30
|
+
INTERNAL_NAMESPACE,
|
|
31
|
+
INTERNAL_INIT_COMMAND,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function formatProductName() {
|
|
36
|
+
return PRODUCT_NAME;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatCliCommand(command) {
|
|
40
|
+
const tail = String(command || "").trim().replace(/^\//, "");
|
|
41
|
+
return tail ? `${CLI_NAME} ${tail}` : CLI_NAME;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatInitCommand() {
|
|
45
|
+
return PUBLIC_INIT_COMMAND;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatPublicUrl(pathname) {
|
|
49
|
+
if (!pathname) return PUBLIC_SITE_URL;
|
|
50
|
+
const suffix = String(pathname).trim();
|
|
51
|
+
if (!suffix) return PUBLIC_SITE_URL;
|
|
52
|
+
if (/^https?:\/\//i.test(suffix)) return suffix;
|
|
53
|
+
return `${PUBLIC_SITE_URL}/${suffix.replace(/^\/+/, "")}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
PRODUCT_NAME,
|
|
58
|
+
CLI_NAME,
|
|
59
|
+
PUBLIC_INIT_COMMAND,
|
|
60
|
+
PUBLIC_DOMAIN,
|
|
61
|
+
PUBLIC_SITE_URL,
|
|
62
|
+
SUPPORT_EMAIL,
|
|
63
|
+
LEGACY_PRODUCT_NAME,
|
|
64
|
+
LEGACY_CLI_NAME,
|
|
65
|
+
LEGACY_FULL_NAME,
|
|
66
|
+
LEGACY_PACKAGE_NAME,
|
|
67
|
+
INTERNAL_NAMESPACE,
|
|
68
|
+
INTERNAL_INIT_COMMAND,
|
|
69
|
+
getBrand,
|
|
70
|
+
formatProductName,
|
|
71
|
+
formatCliCommand,
|
|
72
|
+
formatInitCommand,
|
|
73
|
+
formatPublicUrl,
|
|
74
|
+
};
|