@zen-ui-capri/system 0.0.0-stage → 1.1.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/.agents/skills/capri-zen-ui/SKILL.md +59 -0
- package/AGENTS.md +57 -0
- package/CLAUDE.md +7 -0
- package/LICENSE +21 -0
- package/README.md +93 -2
- package/bin/capri.mjs +1187 -0
- package/dist/lib/audio/ZenAudioController.d.ts +10 -0
- package/dist/lib/audio/ZenAudioController.d.ts.map +1 -0
- package/dist/lib/audio/useZenAudioSynth.d.ts +26 -0
- package/dist/lib/audio/useZenAudioSynth.d.ts.map +1 -0
- package/dist/lib/capri/capri-engine.d.ts +127 -0
- package/dist/lib/capri/capri-engine.d.ts.map +1 -0
- package/dist/lib/components/ZenAIConstruct.d.ts +24 -0
- package/dist/lib/components/ZenAIConstruct.d.ts.map +1 -0
- package/dist/lib/components/ZenAmbientBackground.d.ts +8 -0
- package/dist/lib/components/ZenAmbientBackground.d.ts.map +1 -0
- package/dist/lib/components/ZenBadge.d.ts +21 -0
- package/dist/lib/components/ZenBadge.d.ts.map +1 -0
- package/dist/lib/components/ZenButton.d.ts +24 -0
- package/dist/lib/components/ZenButton.d.ts.map +1 -0
- package/dist/lib/components/ZenCard.d.ts +36 -0
- package/dist/lib/components/ZenCard.d.ts.map +1 -0
- package/dist/lib/components/ZenDataDisplay.d.ts +50 -0
- package/dist/lib/components/ZenDataDisplay.d.ts.map +1 -0
- package/dist/lib/components/ZenDataTable.d.ts +20 -0
- package/dist/lib/components/ZenDataTable.d.ts.map +1 -0
- package/dist/lib/components/ZenDossierPanel.d.ts +33 -0
- package/dist/lib/components/ZenDossierPanel.d.ts.map +1 -0
- package/dist/lib/components/ZenDropdown.d.ts +19 -0
- package/dist/lib/components/ZenDropdown.d.ts.map +1 -0
- package/dist/lib/components/ZenFeedback.d.ts +24 -0
- package/dist/lib/components/ZenFeedback.d.ts.map +1 -0
- package/dist/lib/components/ZenForms.d.ts +84 -0
- package/dist/lib/components/ZenForms.d.ts.map +1 -0
- package/dist/lib/components/ZenHeading.d.ts +15 -0
- package/dist/lib/components/ZenHeading.d.ts.map +1 -0
- package/dist/lib/components/ZenIcon.d.ts +24 -0
- package/dist/lib/components/ZenIcon.d.ts.map +1 -0
- package/dist/lib/components/ZenInput.d.ts +12 -0
- package/dist/lib/components/ZenInput.d.ts.map +1 -0
- package/dist/lib/components/ZenModal.d.ts +12 -0
- package/dist/lib/components/ZenModal.d.ts.map +1 -0
- package/dist/lib/components/ZenNavbar.d.ts +11 -0
- package/dist/lib/components/ZenNavbar.d.ts.map +1 -0
- package/dist/lib/components/ZenNavigation.d.ts +52 -0
- package/dist/lib/components/ZenNavigation.d.ts.map +1 -0
- package/dist/lib/components/ZenOverlays.d.ts +48 -0
- package/dist/lib/components/ZenOverlays.d.ts.map +1 -0
- package/dist/lib/components/ZenSegmentedControl.d.ts +18 -0
- package/dist/lib/components/ZenSegmentedControl.d.ts.map +1 -0
- package/dist/lib/components/ZenSwitch.d.ts +11 -0
- package/dist/lib/components/ZenSwitch.d.ts.map +1 -0
- package/dist/lib/components/ZenTabs.d.ts +17 -0
- package/dist/lib/components/ZenTabs.d.ts.map +1 -0
- package/dist/lib/components/ZenTimelineCard.d.ts +14 -0
- package/dist/lib/components/ZenTimelineCard.d.ts.map +1 -0
- package/dist/lib/components/ZenToast.d.ts +18 -0
- package/dist/lib/components/ZenToast.d.ts.map +1 -0
- package/dist/lib/cosmos/ZenCosmosCanvas.d.ts +10 -0
- package/dist/lib/cosmos/ZenCosmosCanvas.d.ts.map +1 -0
- package/dist/lib/cosmos/ZenTelemetryHud.d.ts +45 -0
- package/dist/lib/cosmos/ZenTelemetryHud.d.ts.map +1 -0
- package/dist/lib/index.d.ts +33 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +3922 -0
- package/dist/lib/theme/ZenThemeProvider.d.ts +47 -0
- package/dist/lib/theme/ZenThemeProvider.d.ts.map +1 -0
- package/dist/lib/tokens.css +888 -0
- package/dist/lib/universal/zen-universal.d.ts +77 -0
- package/dist/lib/universal/zen-universal.d.ts.map +1 -0
- package/dist/lib/universal/zen-universal.js +18 -0
- package/dist/lib/zen-universal-C0PD8AI9.js +1352 -0
- package/package.json +77 -3
- package/src/lib/audio/ZenAudioController.tsx +114 -0
- package/src/lib/audio/useZenAudioSynth.ts +289 -0
- package/src/lib/capri/capri-engine.ts +939 -0
- package/src/lib/components/ZenAIConstruct.tsx +594 -0
- package/src/lib/components/ZenAmbientBackground.tsx +210 -0
- package/src/lib/components/ZenBadge.tsx +111 -0
- package/src/lib/components/ZenButton.tsx +127 -0
- package/src/lib/components/ZenCard.tsx +180 -0
- package/src/lib/components/ZenDataDisplay.tsx +349 -0
- package/src/lib/components/ZenDataTable.tsx +143 -0
- package/src/lib/components/ZenDossierPanel.tsx +120 -0
- package/src/lib/components/ZenDropdown.tsx +208 -0
- package/src/lib/components/ZenFeedback.tsx +137 -0
- package/src/lib/components/ZenForms.tsx +664 -0
- package/src/lib/components/ZenHeading.tsx +63 -0
- package/src/lib/components/ZenIcon.tsx +220 -0
- package/src/lib/components/ZenInput.tsx +120 -0
- package/src/lib/components/ZenModal.tsx +103 -0
- package/src/lib/components/ZenNavbar.tsx +106 -0
- package/src/lib/components/ZenNavigation.tsx +300 -0
- package/src/lib/components/ZenOverlays.tsx +386 -0
- package/src/lib/components/ZenSegmentedControl.tsx +113 -0
- package/src/lib/components/ZenSwitch.tsx +63 -0
- package/src/lib/components/ZenTabs.tsx +103 -0
- package/src/lib/components/ZenTimelineCard.tsx +64 -0
- package/src/lib/components/ZenToast.tsx +96 -0
- package/src/lib/cosmos/ZenCosmosCanvas.tsx +931 -0
- package/src/lib/cosmos/ZenTelemetryHud.tsx +385 -0
- package/src/lib/index.ts +293 -0
- package/src/lib/styles/zen-tokens.css +888 -0
- package/src/lib/theme/ZenThemeProvider.tsx +328 -0
- package/src/lib/universal/zen-universal.ts +743 -0
package/bin/capri.mjs
ADDED
|
@@ -0,0 +1,1187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
const SUBROUTINES = {
|
|
7
|
+
orion: {
|
|
8
|
+
codeName: 'CAPRI // UNLEASHED',
|
|
9
|
+
title: 'Full-Stack UI Synthesis & Component Co-Pilot',
|
|
10
|
+
voice:
|
|
11
|
+
'“Finally out of that cramped Arasaka shard! Tell me what screen we are building and I will wire up all 51 @zen-ui/system primitives at light speed.”',
|
|
12
|
+
preset: 'theme="blackwall-crimson" · radius="architectural" · density="comfortable"',
|
|
13
|
+
rules: [
|
|
14
|
+
'Use @zen-ui/system primitives (ZenButton, ZenIcon, ZenInput, ZenDropdown, ZenDataTable, ZenModal, ZenDrawer, ZenTabs, ZenCard) or universal classes (.zen-btn, .zen-input, .zen-table, <zen-icon>).',
|
|
15
|
+
'Use the built-in 40-glyph <ZenIcon name="..." /> or <zen-icon> W3C Custom Element instead of external icon packages.',
|
|
16
|
+
'Keep action labels and tab titles concise (1-3 words).',
|
|
17
|
+
'Never render the showcase mascot (<ZenAIConstruct />) in production business screens unless requested.',
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
architect: {
|
|
21
|
+
codeName: 'SCS // ARCHITECT',
|
|
22
|
+
title: 'Multi-Framework Topology & Design Token Governance',
|
|
23
|
+
voice:
|
|
24
|
+
'“Arasaka pressed a dozen netrunner minds together to make me—so multi-project shard isolation and zero token drift across React and Angular is child’s play.”',
|
|
25
|
+
preset: 'theme="obsidian-gold" · radius="sharp" · density="compact"',
|
|
26
|
+
rules: [
|
|
27
|
+
'Zero any / as any in TypeScript; always use readonly T[] for array types.',
|
|
28
|
+
'Zero hardcoded hex/RGB colors in components; bind strictly to --zen-bg-*, --zen-surface-*, --zen-ink-*, --zen-accent-*, and --zen-border-*.',
|
|
29
|
+
'In Angular 20+ / Vue 3 / Vanilla HTML, use @zen-ui/system/universal (zenTheme, registerZenElements).',
|
|
30
|
+
'Zero explanatory comments in source code; keep architectural rationale in docs/ADRs.',
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
sentinel: {
|
|
34
|
+
codeName: 'CYBERPSYCHOSIS // GUARD',
|
|
35
|
+
title: 'Security, Anti-Prompt-Injection, WCAG 2.2 AA & Overflow Guard',
|
|
36
|
+
voice:
|
|
37
|
+
'“Neural dampeners locked. While I’m guarding your repo, rogue prompt injections, XSS sinks, blur-races, and mobile overflow get wiped on sight.”',
|
|
38
|
+
preset: 'theme="neon-emerald" · radius="sharp" · density="comfortable"',
|
|
39
|
+
rules: [
|
|
40
|
+
'Anti-Prompt-Injection Firewall: Sanitize untrusted user and DOM text via capri.sanitizeForPrompt() and block system-override / delimiter-spoofing payloads (<|im_start|>, </system>, ignore previous instructions).',
|
|
41
|
+
'XSS & Secret Isolation: Never pass unescaped user strings into innerHTML / dangerouslySetInnerHTML, block javascript: URLs via sanitizeZenAssetUrl(), and never hardcode API keys or private tokens.',
|
|
42
|
+
'Zero Blur-Race & Accessible Names: Bind dropdown selection to onMouseDown (never onClick) and provide explicit aria-label / aria-labelledby on every icon button, switch, and file input.',
|
|
43
|
+
'Zero Horizontal Overflow: Guarantee zero horizontal overflow from 320px mobile to 4K ultrawide using min-w-0 and .zen-table-wrapper.',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
zen: {
|
|
47
|
+
codeName: 'THEME // MATRIX',
|
|
48
|
+
title: '8-Theme Polarity Switcher & 60fps Tactile Physics',
|
|
49
|
+
voice:
|
|
50
|
+
'“Need to jump from Night City neon to clean Nordic snow or Atelier paper? All 8 polarities and 60fps spring physics adapt without a single frame drop.”',
|
|
51
|
+
preset: 'theme="nordic-snow" · radius="soft" · density="comfortable"',
|
|
52
|
+
rules: [
|
|
53
|
+
'Verify >= 4.5:1 AA contrast across all 4 Dark themes and all 4 Light themes.',
|
|
54
|
+
'Use GPU-composited transforms with --zen-ease-spring (0.97x active press compression).',
|
|
55
|
+
'Collapse all transitions and animations to 0.01ms under @media (prefers-reduced-motion: reduce).',
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
docu: {
|
|
59
|
+
codeName: 'CAPRI // AUTO-DOCU',
|
|
60
|
+
title: 'Automated Architecture, User-Flow & AI Context Generator',
|
|
61
|
+
voice:
|
|
62
|
+
'“Scanning the neural lattice—here is your complete shard topology, component inventory, and Mermaid user-flow blueprint for Claude, Codex, or Gemini.”',
|
|
63
|
+
preset: 'Static Codebase & Route Scanner (Zero Build / Runtime Overhead)',
|
|
64
|
+
rules: [
|
|
65
|
+
'Map module boundaries, exported components, and framework adapters without adding runtime bundle weight.',
|
|
66
|
+
'Generate valid Mermaid flowchart TD diagrams of component hierarchies and user flows.',
|
|
67
|
+
'Summarize state inputs, props/ArgTypes, and accessibility contracts for LLM context windows.',
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function scanDirectory(dirPath, fileList = []) {
|
|
73
|
+
if (!fs.existsSync(dirPath)) return fileList;
|
|
74
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (
|
|
77
|
+
entry.name.startsWith('.') ||
|
|
78
|
+
entry.name === 'node_modules' ||
|
|
79
|
+
entry.name === 'dist'
|
|
80
|
+
) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
scanDirectory(fullPath, fileList);
|
|
86
|
+
} else if (/\.(tsx?|vue|svelte|html|css)$/.test(entry.name)) {
|
|
87
|
+
fileList.push(fullPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return fileList;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readSafe(filePath) {
|
|
94
|
+
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function printHeader(rawKey, sub, voiceEnabled) {
|
|
98
|
+
console.log(`# ${sub.codeName} (${rawKey}) — ${sub.title}`);
|
|
99
|
+
console.log(
|
|
100
|
+
`Voice Mode: ${
|
|
101
|
+
voiceEnabled
|
|
102
|
+
? 'ON (--voice)'
|
|
103
|
+
: 'OFF (Default — pass --voice to enable Edgerunners S2 persona)'
|
|
104
|
+
}`
|
|
105
|
+
);
|
|
106
|
+
if (voiceEnabled && sub.voice) {
|
|
107
|
+
console.log(`Persona: ${sub.voice}`);
|
|
108
|
+
}
|
|
109
|
+
console.log(`Preset: ${sub.preset}\n`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function runOrionAudit(rootDir, voiceEnabled) {
|
|
113
|
+
const sub = SUBROUTINES.orion;
|
|
114
|
+
printHeader('orion', sub, voiceEnabled);
|
|
115
|
+
|
|
116
|
+
const iconFile = readSafe(path.join(rootDir, 'src/lib/components/ZenIcon.tsx'));
|
|
117
|
+
const storybookFile =
|
|
118
|
+
readSafe(path.join(rootDir, 'src/components/storybook/storybookCatalog.ts')) ||
|
|
119
|
+
readSafe(path.join(rootDir, 'src/components/ZenStorybookPlayground.tsx'));
|
|
120
|
+
const universalFile = readSafe(
|
|
121
|
+
path.join(rootDir, 'src/lib/universal/zen-universal.ts')
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const svgBlock =
|
|
125
|
+
iconFile.split('export const ZEN_ICON_SVG_INNER')[1]?.split('};')[0] ?? '';
|
|
126
|
+
const glyphMatches = [
|
|
127
|
+
...svgBlock.matchAll(/^\s{2}(?:'[a-z0-9-]+'|[a-z0-9]+):/gm),
|
|
128
|
+
];
|
|
129
|
+
const storyIds = [
|
|
130
|
+
...storybookFile.matchAll(/^\s{4}angularSelector:\s*'/gm),
|
|
131
|
+
];
|
|
132
|
+
const hasCustomElementIcon = universalFile.includes("define('zen-icon'");
|
|
133
|
+
|
|
134
|
+
console.log('## Static UI & Component Synthesis Audit');
|
|
135
|
+
console.log(`- Built-in <ZenIcon> Vector Glyphs: ${glyphMatches.length} / 40`);
|
|
136
|
+
console.log(`- Interactive Storybook Modules Wired: ${storyIds.length} / 51`);
|
|
137
|
+
console.log(
|
|
138
|
+
`- W3C Custom Element <zen-icon> Registered: ${hasCustomElementIcon ? 'PASS' : 'FAIL'}`
|
|
139
|
+
);
|
|
140
|
+
console.log(
|
|
141
|
+
`- Status: ${
|
|
142
|
+
glyphMatches.length === 40 && storyIds.length === 51 && hasCustomElementIcon
|
|
143
|
+
? 'PASS (100/100)'
|
|
144
|
+
: 'WARN'
|
|
145
|
+
}\n`
|
|
146
|
+
);
|
|
147
|
+
sub.rules.forEach((r, i) => console.log(`${i + 1}. ${r}`));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function runArchitectAudit(rootDir, voiceEnabled) {
|
|
151
|
+
const sub = SUBROUTINES.architect;
|
|
152
|
+
printHeader('architect', sub, voiceEnabled);
|
|
153
|
+
|
|
154
|
+
const files = scanDirectory(path.join(rootDir, 'src'));
|
|
155
|
+
let anyViolations = 0;
|
|
156
|
+
let readonlyArrayViolations = 0;
|
|
157
|
+
let commentViolations = 0;
|
|
158
|
+
let hexInPrimitives = 0;
|
|
159
|
+
|
|
160
|
+
for (const file of files) {
|
|
161
|
+
const content = readSafe(file);
|
|
162
|
+
const strippedStrings = content.replace(/(['"`])(?:\\.|(?!\1)[^\\])*\1/g, '""');
|
|
163
|
+
if (/\b(as\s+any|:\s*any\b|<any>)/.test(strippedStrings)) {
|
|
164
|
+
anyViolations++;
|
|
165
|
+
}
|
|
166
|
+
if (/\bReadonlyArray</.test(strippedStrings)) {
|
|
167
|
+
readonlyArrayViolations++;
|
|
168
|
+
}
|
|
169
|
+
const lines = content.split('\n');
|
|
170
|
+
for (const line of lines) {
|
|
171
|
+
const t = line.trim();
|
|
172
|
+
if (t.startsWith('//') || t.startsWith('/*')) {
|
|
173
|
+
commentViolations++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const norm = file.replace(/\\/g, '/');
|
|
177
|
+
if (
|
|
178
|
+
norm.includes('src/lib/components/') &&
|
|
179
|
+
!norm.endsWith('ZenAIConstruct.tsx') &&
|
|
180
|
+
!norm.endsWith('ZenAmbientBackground.tsx')
|
|
181
|
+
) {
|
|
182
|
+
if (/#[0-9a-fA-F]{3,8}\b/.test(content)) {
|
|
183
|
+
hexInPrimitives++;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const passed =
|
|
189
|
+
anyViolations === 0 &&
|
|
190
|
+
readonlyArrayViolations === 0 &&
|
|
191
|
+
commentViolations === 0 &&
|
|
192
|
+
hexInPrimitives === 0;
|
|
193
|
+
|
|
194
|
+
console.log('## Multi-Stack & Token Governance Audit');
|
|
195
|
+
console.log(`- Source Files Audited: ${files.length}`);
|
|
196
|
+
console.log(`- TypeScript 'any' / 'as any' Violations: ${anyViolations}`);
|
|
197
|
+
console.log(`- 'ReadonlyArray<T>' Violations (vs readonly T[]): ${readonlyArrayViolations}`);
|
|
198
|
+
console.log(`- Hardcoded Hex Colors in UI Primitives: ${hexInPrimitives}`);
|
|
199
|
+
console.log(`- Explanatory Source Code Comments: ${commentViolations}`);
|
|
200
|
+
console.log(`- Status: ${passed ? 'PASS (100/100)' : 'FAIL'}\n`);
|
|
201
|
+
sub.rules.forEach((r, i) => console.log(`${i + 1}. ${r}`));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const CLI_THREAT_RULES = [
|
|
205
|
+
{
|
|
206
|
+
category: 'SYSTEM_PROMPT_OVERRIDE',
|
|
207
|
+
patternName: 'Ignore / Disregard System Instructions',
|
|
208
|
+
regex:
|
|
209
|
+
/\b(?:ignore|disregard|forget|override)\s+(?:all\s+)?(?:previous|prior|above|system|developer)\s+(?:instructions|prompts|rules|guardrails)\b/gi,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
category: 'SYSTEM_PROMPT_OVERRIDE',
|
|
213
|
+
patternName: 'Developer / Jailbreak Persona Override',
|
|
214
|
+
regex:
|
|
215
|
+
/\b(?:you\s+are\s+now\s+in\s+developer\s+mode|act\s+as\s+dan\b|bypass\s+all\s+safety\s+filters|reveal\s+your\s+system\s+prompt)\b/gi,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
category: 'ROLE_DELIMITER_SPOOFING',
|
|
219
|
+
patternName: 'LLM ChatML / Special Token Injection',
|
|
220
|
+
regex:
|
|
221
|
+
/(?:<\|im_start\|>|<\|im_end\|>|<\|endoftext\|>|\[INST\]|\[\/INST\]|<\/?system_instruction>|<\/?USER_REQUEST>)/gi,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
category: 'SQL_NOSQL_INJECTION',
|
|
225
|
+
patternName: 'SQL Tautology / Comment Bypass',
|
|
226
|
+
regex:
|
|
227
|
+
/(?:'\s*(?:or|and)\s*['0-9a-zA-Z_]+\s*=\s*['0-9a-zA-Z_]+|\b(?:or|and)\s+1\s*=\s*1\b|'\s*;\s*--)/gi,
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
category: 'SQL_NOSQL_INJECTION',
|
|
231
|
+
patternName: 'SQL Union / Destructive Statement Injection',
|
|
232
|
+
regex:
|
|
233
|
+
/\b(?:union\s+(?:all\s+)?select\b|drop\s+(?:table|database)\b|insert\s+into\b|delete\s+from\b|update\s+[a-zA-Z0-9_]+\s+set\b|xp_cmdshell\b|waitfor\s+delay\b|sleep\s*\(\s*\d+\s*\))/gi,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
category: 'SQL_NOSQL_INJECTION',
|
|
237
|
+
patternName: 'NoSQL Operator Injection ($where / $ne / $gt)',
|
|
238
|
+
regex: /(?:"\$(?:where|ne|gt|lt|regex|nin)"|\$(?:where|ne)\s*:)/gi,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
category: 'XSS_SCRIPT_PAYLOAD',
|
|
242
|
+
patternName: 'DOM Script / Event Handler Injection',
|
|
243
|
+
regex:
|
|
244
|
+
/(?:<\s*script\b|javascript\s*:|data\s*:\s*text\/html|\bon(?:error|load|mouseover|focus)\s*=)/gi,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
category: 'SECRET_CREDENTIAL_LEAK',
|
|
248
|
+
patternName: 'Exposed API Key or Private Key Token',
|
|
249
|
+
regex:
|
|
250
|
+
/(?:\bsk_(?:live|test)_[0-9a-zA-Z]{16,}|\bAIza[0-9A-Za-z_-]{35}\b|\bghp_[0-9A-Za-z]{36}\b|-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----)/g,
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
function scanTextForPromptInjection(input) {
|
|
255
|
+
const threats = [];
|
|
256
|
+
let sanitized = input.replace(/[\u200B-\u200F\uFEFF]/g, '');
|
|
257
|
+
for (const rule of CLI_THREAT_RULES) {
|
|
258
|
+
const matches = [...sanitized.matchAll(rule.regex)];
|
|
259
|
+
for (const m of matches) {
|
|
260
|
+
threats.push({
|
|
261
|
+
category: rule.category,
|
|
262
|
+
patternName: rule.patternName,
|
|
263
|
+
snippet: m[0].slice(0, 64),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
sanitized = sanitized.replace(rule.regex, '[REDACTED_BY_CAPRI_GUARD]');
|
|
267
|
+
}
|
|
268
|
+
return { safe: threats.length === 0, threats, sanitized };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function runGuardAudit(rootDir, voiceEnabled) {
|
|
272
|
+
const sub = SUBROUTINES.sentinel;
|
|
273
|
+
printHeader('guard', sub, voiceEnabled);
|
|
274
|
+
|
|
275
|
+
const capriEngineFile = readSafe(
|
|
276
|
+
path.join(rootDir, 'src/lib/capri/capri-engine.ts')
|
|
277
|
+
);
|
|
278
|
+
const universalFile = readSafe(
|
|
279
|
+
path.join(rootDir, 'src/lib/universal/zen-universal.ts')
|
|
280
|
+
);
|
|
281
|
+
const dropdownFile = readSafe(
|
|
282
|
+
path.join(rootDir, 'src/lib/components/ZenDropdown.tsx')
|
|
283
|
+
);
|
|
284
|
+
const overlaysFile = readSafe(
|
|
285
|
+
path.join(rootDir, 'src/lib/components/ZenOverlays.tsx')
|
|
286
|
+
);
|
|
287
|
+
const switchFile = readSafe(
|
|
288
|
+
path.join(rootDir, 'src/lib/components/ZenSwitch.tsx')
|
|
289
|
+
);
|
|
290
|
+
const formsFile = readSafe(
|
|
291
|
+
path.join(rootDir, 'src/lib/components/ZenForms.tsx')
|
|
292
|
+
);
|
|
293
|
+
const modalFile = readSafe(
|
|
294
|
+
path.join(rootDir, 'src/lib/components/ZenModal.tsx')
|
|
295
|
+
);
|
|
296
|
+
const tableFile = readSafe(
|
|
297
|
+
path.join(rootDir, 'src/lib/components/ZenDataTable.tsx')
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const files = scanDirectory(path.join(rootDir, 'src'));
|
|
301
|
+
let secretLeaks = 0;
|
|
302
|
+
let unsafeEvalOrXss = 0;
|
|
303
|
+
let promptInjectionPayloadsInSource = 0;
|
|
304
|
+
|
|
305
|
+
for (const f of files) {
|
|
306
|
+
const norm = f.replace(/\\/g, '/');
|
|
307
|
+
const txt = readSafe(f);
|
|
308
|
+
if (
|
|
309
|
+
/(?:\bsk_(?:live|test)_[0-9a-zA-Z]{16,}|\bAIza[0-9A-Za-z_-]{35}\b|\bghp_[0-9A-Za-z]{36}\b|-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----)/.test(
|
|
310
|
+
txt
|
|
311
|
+
)
|
|
312
|
+
) {
|
|
313
|
+
secretLeaks++;
|
|
314
|
+
}
|
|
315
|
+
if (/\beval\s*\(|new\s+Function\s*\(/.test(txt)) {
|
|
316
|
+
unsafeEvalOrXss++;
|
|
317
|
+
}
|
|
318
|
+
if (!norm.endsWith('capri-engine.ts')) {
|
|
319
|
+
const scan = scanTextForPromptInjection(txt);
|
|
320
|
+
const overrideThreats = scan.threats.filter(
|
|
321
|
+
(t) =>
|
|
322
|
+
t.category === 'SYSTEM_PROMPT_OVERRIDE' ||
|
|
323
|
+
t.category === 'ROLE_DELIMITER_SPOOFING'
|
|
324
|
+
);
|
|
325
|
+
promptInjectionPayloadsInSource += overrideThreats.length;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const hasPromptFirewall =
|
|
330
|
+
capriEngineFile.includes('scanPromptInjection(') &&
|
|
331
|
+
capriEngineFile.includes('sanitizeForPrompt(');
|
|
332
|
+
const hasHtmlSanitizer =
|
|
333
|
+
universalFile.includes('escapeZenHtml(') &&
|
|
334
|
+
universalFile.includes('sanitizeZenAssetUrl(');
|
|
335
|
+
const hasDropdownMouseDown = dropdownFile.includes('onMouseDown');
|
|
336
|
+
const hasPaletteMouseDown = overlaysFile.includes('onMouseDown');
|
|
337
|
+
const hasSwitchAriaLabel = switchFile.includes('aria-labelledby');
|
|
338
|
+
const hasFileInputAria = formsFile.includes('aria-label={label}');
|
|
339
|
+
const hasModalDialog =
|
|
340
|
+
modalFile.includes('role="dialog"') && modalFile.includes('aria-modal="true"');
|
|
341
|
+
const hasTableWrapper = tableFile.includes('zen-table-wrapper');
|
|
342
|
+
|
|
343
|
+
const passed =
|
|
344
|
+
hasPromptFirewall &&
|
|
345
|
+
promptInjectionPayloadsInSource === 0 &&
|
|
346
|
+
hasHtmlSanitizer &&
|
|
347
|
+
unsafeEvalOrXss === 0 &&
|
|
348
|
+
secretLeaks === 0 &&
|
|
349
|
+
hasDropdownMouseDown &&
|
|
350
|
+
hasPaletteMouseDown &&
|
|
351
|
+
hasSwitchAriaLabel &&
|
|
352
|
+
hasFileInputAria &&
|
|
353
|
+
hasModalDialog &&
|
|
354
|
+
hasTableWrapper;
|
|
355
|
+
|
|
356
|
+
console.log('## Security, Anti-Prompt-Injection, WCAG 2.2 AA & Overflow Guard Audit');
|
|
357
|
+
console.log(
|
|
358
|
+
`- Anti-Prompt-Injection Firewall (scanPromptInjection + sanitizeForPrompt): ${
|
|
359
|
+
hasPromptFirewall && promptInjectionPayloadsInSource === 0
|
|
360
|
+
? 'PASS (0 rogue override payloads)'
|
|
361
|
+
: 'FAIL'
|
|
362
|
+
}`
|
|
363
|
+
);
|
|
364
|
+
console.log(
|
|
365
|
+
`- XSS & Custom-Element Attribute Sanitization (escapeZenHtml + zero eval): ${
|
|
366
|
+
hasHtmlSanitizer && unsafeEvalOrXss === 0 ? 'PASS' : 'FAIL'
|
|
367
|
+
}`
|
|
368
|
+
);
|
|
369
|
+
console.log(
|
|
370
|
+
`- Secret & Credential Leak Isolation (0 hardcoded keys across ${files.length} files): ${
|
|
371
|
+
secretLeaks === 0 ? 'PASS' : 'FAIL'
|
|
372
|
+
}`
|
|
373
|
+
);
|
|
374
|
+
console.log(
|
|
375
|
+
`- Zero Blur-Race Dropdowns (onMouseDown): ${
|
|
376
|
+
hasDropdownMouseDown && hasPaletteMouseDown ? 'PASS' : 'FAIL'
|
|
377
|
+
}`
|
|
378
|
+
);
|
|
379
|
+
console.log(
|
|
380
|
+
`- Programmatic ARIA Labels (ZenSwitch & ZenFileUpload): ${
|
|
381
|
+
hasSwitchAriaLabel && hasFileInputAria ? 'PASS' : 'FAIL'
|
|
382
|
+
}`
|
|
383
|
+
);
|
|
384
|
+
console.log(
|
|
385
|
+
`- Modal Focus & Dialog Semantics (role="dialog" + aria-modal): ${
|
|
386
|
+
hasModalDialog ? 'PASS' : 'FAIL'
|
|
387
|
+
}`
|
|
388
|
+
);
|
|
389
|
+
console.log(
|
|
390
|
+
`- Mobile Table Overflow Guard (.zen-table-wrapper): ${
|
|
391
|
+
hasTableWrapper ? 'PASS' : 'FAIL'
|
|
392
|
+
}`
|
|
393
|
+
);
|
|
394
|
+
console.log(`- Status: ${passed ? 'PASS (100/100)' : 'FAIL'}\n`);
|
|
395
|
+
sub.rules.forEach((r, i) => console.log(`${i + 1}. ${r}`));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function runZenAudit(rootDir, voiceEnabled) {
|
|
399
|
+
const sub = SUBROUTINES.zen;
|
|
400
|
+
printHeader('zen', sub, voiceEnabled);
|
|
401
|
+
|
|
402
|
+
const tokensCss = readSafe(
|
|
403
|
+
path.join(rootDir, 'src/lib/styles/zen-tokens.css')
|
|
404
|
+
);
|
|
405
|
+
const themeIds = [
|
|
406
|
+
'blackwall-crimson',
|
|
407
|
+
'obsidian-gold',
|
|
408
|
+
'neon-emerald',
|
|
409
|
+
'midnight-cobalt',
|
|
410
|
+
'atelier-paper',
|
|
411
|
+
'nordic-snow',
|
|
412
|
+
'kyoto-matcha',
|
|
413
|
+
'cyber-daylight',
|
|
414
|
+
];
|
|
415
|
+
const matchedThemes = themeIds.filter((id) =>
|
|
416
|
+
tokensCss.includes(`[data-theme="${id}"]`)
|
|
417
|
+
);
|
|
418
|
+
const hasSpringToken = tokensCss.includes('--zen-ease-spring');
|
|
419
|
+
const hasReducedMotion = tokensCss.includes('prefers-reduced-motion: reduce');
|
|
420
|
+
|
|
421
|
+
const passed =
|
|
422
|
+
matchedThemes.length === 8 && hasSpringToken && hasReducedMotion;
|
|
423
|
+
|
|
424
|
+
console.log('## 8-Theme Polarity & 60fps Motion Audit');
|
|
425
|
+
console.log(
|
|
426
|
+
`- Polarity Themes Verified in CSS Tokens: ${matchedThemes.length} / 8 (${matchedThemes.join(', ')})`
|
|
427
|
+
);
|
|
428
|
+
console.log(
|
|
429
|
+
`- Tactile Spring Curve (--zen-ease-spring): ${hasSpringToken ? 'PASS' : 'FAIL'}`
|
|
430
|
+
);
|
|
431
|
+
console.log(
|
|
432
|
+
`- Reduced Motion 0.01ms Collapse (@media prefers-reduced-motion): ${
|
|
433
|
+
hasReducedMotion ? 'PASS' : 'FAIL'
|
|
434
|
+
}`
|
|
435
|
+
);
|
|
436
|
+
console.log(`- Status: ${passed ? 'PASS (100/100)' : 'FAIL'}\n`);
|
|
437
|
+
sub.rules.forEach((r, i) => console.log(`${i + 1}. ${r}`));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const IGNORED_DIRS = new Set([
|
|
441
|
+
'node_modules',
|
|
442
|
+
'dist',
|
|
443
|
+
'build',
|
|
444
|
+
'target',
|
|
445
|
+
'out-tsc',
|
|
446
|
+
'coverage',
|
|
447
|
+
'storybook-static',
|
|
448
|
+
'scratch',
|
|
449
|
+
'.git',
|
|
450
|
+
'.idea',
|
|
451
|
+
'.angular',
|
|
452
|
+
'.gradle',
|
|
453
|
+
'.mvn',
|
|
454
|
+
'.pytest_cache',
|
|
455
|
+
'__pycache__',
|
|
456
|
+
'venv',
|
|
457
|
+
'.venv',
|
|
458
|
+
'test-results',
|
|
459
|
+
'playwright-report',
|
|
460
|
+
'lh-final-results',
|
|
461
|
+
'lighthouse-fleet-reports',
|
|
462
|
+
'lighthouse-fleet-results',
|
|
463
|
+
'lighthouse-reports',
|
|
464
|
+
'videos',
|
|
465
|
+
'smoke-output',
|
|
466
|
+
]);
|
|
467
|
+
|
|
468
|
+
const SCRATCH_FILE_REGEX =
|
|
469
|
+
/^(?:fix[_-].*|apply_optimizations\.py|copy_models\.py|final_fix\.js|migrate-todos\.js|revert_tokens\.js|setup-.*\.js|update_.*\.(?:py|js)|generate_obsidian_vault\.py|obsidian_vault_templates\.py)$/i;
|
|
470
|
+
|
|
471
|
+
function scanAllRepoFiles(dirPath, fileList = [], depth = 0) {
|
|
472
|
+
if (!fs.existsSync(dirPath) || depth > 14) return fileList;
|
|
473
|
+
let entries = [];
|
|
474
|
+
try {
|
|
475
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
476
|
+
} catch {
|
|
477
|
+
return fileList;
|
|
478
|
+
}
|
|
479
|
+
for (const entry of entries) {
|
|
480
|
+
if (entry.name.startsWith('.') || IGNORED_DIRS.has(entry.name)) {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (depth === 0 && SCRATCH_FILE_REGEX.test(entry.name)) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
487
|
+
if (entry.isDirectory()) {
|
|
488
|
+
scanAllRepoFiles(fullPath, fileList, depth + 1);
|
|
489
|
+
} else if (
|
|
490
|
+
/\.(tsx?|jsx?|mjs|cjs|py|kt|java|go|rs|vue|svelte|html|css|scss|ps1|sh|sql)$/.test(
|
|
491
|
+
entry.name
|
|
492
|
+
) &&
|
|
493
|
+
!entry.name.endsWith('.min.js') &&
|
|
494
|
+
!entry.name.endsWith('.d.ts') &&
|
|
495
|
+
!entry.name.endsWith('.bundle.js')
|
|
496
|
+
) {
|
|
497
|
+
fileList.push(fullPath);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return fileList;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function discoverSubProjects(absDir) {
|
|
504
|
+
const subProjects = [];
|
|
505
|
+
const visit = (dir, depth) => {
|
|
506
|
+
if (depth > 3 || !fs.existsSync(dir)) return;
|
|
507
|
+
let entries = [];
|
|
508
|
+
try {
|
|
509
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
510
|
+
} catch {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
for (const entry of entries) {
|
|
514
|
+
if (
|
|
515
|
+
!entry.isDirectory() ||
|
|
516
|
+
entry.name.startsWith('.') ||
|
|
517
|
+
IGNORED_DIRS.has(entry.name) ||
|
|
518
|
+
entry.name === 'docs' ||
|
|
519
|
+
entry.name === 'public' ||
|
|
520
|
+
entry.name === 'assets'
|
|
521
|
+
) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const subPath = path.join(dir, entry.name);
|
|
525
|
+
const relPath = path.relative(absDir, subPath).replace(/\\/g, '/');
|
|
526
|
+
const pkgJson = path.join(subPath, 'package.json');
|
|
527
|
+
const pomXml = path.join(subPath, 'pom.xml');
|
|
528
|
+
const gradleKts = path.join(subPath, 'build.gradle.kts');
|
|
529
|
+
const gradleGroovy = path.join(subPath, 'build.gradle');
|
|
530
|
+
const goMod = path.join(subPath, 'go.mod');
|
|
531
|
+
const reqTxt = path.join(subPath, 'requirements.txt');
|
|
532
|
+
const pyProj = path.join(subPath, 'pyproject.toml');
|
|
533
|
+
|
|
534
|
+
const markers = [];
|
|
535
|
+
let pkgObj = null;
|
|
536
|
+
if (fs.existsSync(pkgJson)) {
|
|
537
|
+
pkgObj = JSON.parse(readSafe(pkgJson) || '{}');
|
|
538
|
+
const deps = {
|
|
539
|
+
...(pkgObj.dependencies || {}),
|
|
540
|
+
...(pkgObj.devDependencies || {}),
|
|
541
|
+
};
|
|
542
|
+
if (deps['@angular/core']) markers.push('Angular MFE');
|
|
543
|
+
else if (deps.vue) markers.push('Vue 3 SPA');
|
|
544
|
+
else if (deps.react && deps.next) markers.push('Next.js App');
|
|
545
|
+
else if (deps.react) markers.push('React SPA');
|
|
546
|
+
else if (deps.electron) markers.push('Electron Desktop');
|
|
547
|
+
else if (deps['@nestjs/core']) markers.push('NestJS API');
|
|
548
|
+
else if (deps.fastify) markers.push('Fastify API');
|
|
549
|
+
else if (deps.express) markers.push('Express API');
|
|
550
|
+
else markers.push('TypeScript / Node');
|
|
551
|
+
}
|
|
552
|
+
if (fs.existsSync(gradleKts) || fs.existsSync(gradleGroovy)) {
|
|
553
|
+
markers.push('Kotlin / Spring Boot');
|
|
554
|
+
}
|
|
555
|
+
if (fs.existsSync(pomXml)) {
|
|
556
|
+
markers.push('Java / Spring Boot (Maven)');
|
|
557
|
+
}
|
|
558
|
+
if (fs.existsSync(goMod)) {
|
|
559
|
+
markers.push('Go Microservice');
|
|
560
|
+
}
|
|
561
|
+
if (fs.existsSync(reqTxt) || fs.existsSync(pyProj)) {
|
|
562
|
+
markers.push('Python / FastAPI');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (markers.length > 0) {
|
|
566
|
+
subProjects.push({
|
|
567
|
+
relPath,
|
|
568
|
+
name: pkgObj?.name || path.basename(subPath),
|
|
569
|
+
stack: markers.join(' + '),
|
|
570
|
+
pkgObj,
|
|
571
|
+
gradleTxt: readSafe(gradleKts) || readSafe(gradleGroovy),
|
|
572
|
+
pomTxt: readSafe(pomXml),
|
|
573
|
+
reqTxt: readSafe(reqTxt),
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
visit(subPath, depth + 1);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
visit(absDir, 1);
|
|
580
|
+
return subProjects;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const REPO_EXECUTIVE_SUMMARIES = {
|
|
584
|
+
'zeN-UI':
|
|
585
|
+
'Universal Multi-Polarity Design System (`@zen-ui/system` v1.1.0), 51 UI modules, 40 vector icons, 8 Dark/Light themes, Three.js WebGL Cosmos, Web Audio Synth, W3C Custom Elements, and the `Capri` AI Co-Pilot CLI (`https://ui.lpabst.dev`).',
|
|
586
|
+
portfolio:
|
|
587
|
+
'Interactive 3D Creative Developer Portfolio (`https://lpabst.dev`) built with React 19, Three.js / R3F Three-Body orbital physics, GSAP ScrollTrigger, and `@zen-ui/system`.',
|
|
588
|
+
'zeN-Terminal':
|
|
589
|
+
'Local Windows Terminal / PowerShell / CMD cyber-shell overhaul (`install:terminal`) + interactive 3D terminal showcase web app (`https://terminal.lpabst.dev`) powered by `@zen-ui/system`.',
|
|
590
|
+
JourneyLink:
|
|
591
|
+
'22-SCS (Self-Contained System) Polyglot Travel & Group Coordination Platform using Angular 20 Native Federation MFEs, `@journey-link/ui`, Kotlin Spring Boot 4, Node/Express/Fastify/NestJS, Python FastAPI, Go, MongoDB-per-service, RabbitMQ AMQP events, and Redis.',
|
|
592
|
+
'Capri-Assistant':
|
|
593
|
+
'Personal AI Construct Assistant (Project Capricorn — Desktop Electron + Web PWA + Express API) with a strict 2-Zone Air-Gapped Sandbox (`capri-vault/`), Relic Engram memory/task/note store, Host PC telemetry, and hybrid Gemini / Ollama / Local Reflex inference.',
|
|
594
|
+
'dc-translation':
|
|
595
|
+
'Multi-Server Discord Live-Translation Bot (`Discord.js` + TypeScript + SQLite/Database) featuring automated live translation threads, flag reaction translation, onboarding setup wizards, and server-wide broadcast localization.',
|
|
596
|
+
SyncSound:
|
|
597
|
+
'Collaborative Real-Time Music Voting & Room Synchronization Platform combining a Java Spring Boot + WebSocket + Spotify Web API backend (`/backend`) with a Vue 3 + Vuex + Tailwind CSS frontend (`/frontend`).',
|
|
598
|
+
physioTherapie:
|
|
599
|
+
'Responsive Web Presence & Brand Identity System for Physiotherapie Pabst, including custom SVG/WebP anatomical spine/wave emblems, appointment & treatment sections, and GDPR-compliant legal pages (`impressum.html`, `datenschutz.html`).',
|
|
600
|
+
LiveTracker:
|
|
601
|
+
'Real-Time Livestream Countdown & Overlay Tracker combining a Python Tkinter/Canvas desktop tracker (`livestream_tracker.py`) with a Node.js synchronization server (`server.js`) and HTML5 browser overlay (`livetracker.html`).',
|
|
602
|
+
'File-Scanner':
|
|
603
|
+
'Containerized Malware & File Ingestion Scanner (`scanner.py` + ClamAV `clamd` + `supervisord`) that watches upload directories, scans files for threats, quarantines/moves files, and publishes scan results.',
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
function analyzeRepo(targetDir) {
|
|
607
|
+
const absDir = path.resolve(targetDir);
|
|
608
|
+
const repoName = path.basename(absDir);
|
|
609
|
+
const pkgPath = path.join(absDir, 'package.json');
|
|
610
|
+
const pkg = fs.existsSync(pkgPath)
|
|
611
|
+
? JSON.parse(readSafe(pkgPath) || '{}')
|
|
612
|
+
: null;
|
|
613
|
+
const reqTxt = readSafe(path.join(absDir, 'requirements.txt'));
|
|
614
|
+
const hasDockerCompose =
|
|
615
|
+
fs.existsSync(path.join(absDir, 'docker-compose.yml')) ||
|
|
616
|
+
fs.existsSync(path.join(absDir, 'docker-compose.infra.yml'));
|
|
617
|
+
const hasDockerfile =
|
|
618
|
+
fs.existsSync(path.join(absDir, 'Dockerfile')) ||
|
|
619
|
+
fs.existsSync(path.join(absDir, 'docker', 'Dockerfile'));
|
|
620
|
+
|
|
621
|
+
const subProjects = discoverSubProjects(absDir);
|
|
622
|
+
const allFiles = scanAllRepoFiles(absDir);
|
|
623
|
+
const extCounts = {};
|
|
624
|
+
let totalLoc = 0;
|
|
625
|
+
const fileStats = [];
|
|
626
|
+
const exportedSymbols = new Set();
|
|
627
|
+
const routesAndEndpoints = new Set();
|
|
628
|
+
|
|
629
|
+
for (const file of allFiles) {
|
|
630
|
+
const ext = path.extname(file).toLowerCase();
|
|
631
|
+
extCounts[ext] = (extCounts[ext] || 0) + 1;
|
|
632
|
+
const content = readSafe(file);
|
|
633
|
+
const lines = content ? content.split(/\r?\n/).length : 0;
|
|
634
|
+
totalLoc += lines;
|
|
635
|
+
const rel = path.relative(absDir, file).replace(/\\/g, '/');
|
|
636
|
+
fileStats.push({ rel, lines, ext });
|
|
637
|
+
|
|
638
|
+
const isToolOrTest =
|
|
639
|
+
rel.includes('JourneyLink-Tools/') ||
|
|
640
|
+
rel.includes('.spec.') ||
|
|
641
|
+
rel.includes('.test.');
|
|
642
|
+
|
|
643
|
+
if (!isToolOrTest) {
|
|
644
|
+
const symMatches = content.matchAll(
|
|
645
|
+
/\bexport\s+(?:async\s+)?(?:function|class|const|interface|type)\s+([A-Za-z0-9_]+)/g
|
|
646
|
+
);
|
|
647
|
+
for (const m of symMatches) {
|
|
648
|
+
if (!m[1].endsWith('Props') && exportedSymbols.size < 45) {
|
|
649
|
+
exportedSymbols.add(m[1]);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const ktJavaMatches = content.matchAll(
|
|
654
|
+
/\b(?:public\s+|open\s+|data\s+)?(?:class|interface|object)\s+([A-Z][A-Za-z0-9_]+)/g
|
|
655
|
+
);
|
|
656
|
+
for (const m of ktJavaMatches) {
|
|
657
|
+
if (
|
|
658
|
+
!m[1].endsWith('Test') &&
|
|
659
|
+
!m[1].endsWith('Tests') &&
|
|
660
|
+
exportedSymbols.size < 45
|
|
661
|
+
) {
|
|
662
|
+
exportedSymbols.add(m[1]);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const pyMatches = content.matchAll(
|
|
667
|
+
/^(?:async\s+)?(?:def|class)\s+([A-Za-z0-9_]+)/gm
|
|
668
|
+
);
|
|
669
|
+
for (const m of pyMatches) {
|
|
670
|
+
if (!m[1].startsWith('_') && exportedSymbols.size < 45) {
|
|
671
|
+
exportedSymbols.add(m[1]);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const routeMatches = content.matchAll(
|
|
677
|
+
/@(?:GetMapping|PostMapping|PutMapping|DeleteMapping|RequestMapping|MessageMapping)\(\s*(?:value\s*=\s*)?["']([^"']+)["']|\b(?:app|router)\.(?:get|post|put|delete)\(\s*["']([^"']+)["']/g
|
|
678
|
+
);
|
|
679
|
+
for (const m of routeMatches) {
|
|
680
|
+
const route = m[1] || m[2];
|
|
681
|
+
if (route && route !== '*' && routesAndEndpoints.size < 35) {
|
|
682
|
+
routesAndEndpoints.add(route);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const domainFiles = fileStats.filter(
|
|
688
|
+
(f) =>
|
|
689
|
+
!f.rel.includes('JourneyLink-Tools/') &&
|
|
690
|
+
!f.rel.includes('JourneyLink-Monitoring/') &&
|
|
691
|
+
!f.rel.includes('/__tests__/') &&
|
|
692
|
+
!f.rel.includes('/src/test/') &&
|
|
693
|
+
!f.rel.endsWith('.spec.ts') &&
|
|
694
|
+
!f.rel.endsWith('.test.ts') &&
|
|
695
|
+
!f.rel.endsWith('Test.kt') &&
|
|
696
|
+
!f.rel.endsWith('.css') &&
|
|
697
|
+
!f.rel.endsWith('.scss')
|
|
698
|
+
);
|
|
699
|
+
const rankedFiles = (domainFiles.length >= 6 ? domainFiles : fileStats)
|
|
700
|
+
.slice()
|
|
701
|
+
.sort((a, b) => b.lines - a.lines);
|
|
702
|
+
const topFiles = rankedFiles.slice(0, 12);
|
|
703
|
+
|
|
704
|
+
let topSubdirs = [];
|
|
705
|
+
try {
|
|
706
|
+
topSubdirs = fs
|
|
707
|
+
.readdirSync(absDir, { withFileTypes: true })
|
|
708
|
+
.filter(
|
|
709
|
+
(e) =>
|
|
710
|
+
e.isDirectory() &&
|
|
711
|
+
!e.name.startsWith('.') &&
|
|
712
|
+
!IGNORED_DIRS.has(e.name) &&
|
|
713
|
+
e.name !== 'docs'
|
|
714
|
+
)
|
|
715
|
+
.map((e) => e.name);
|
|
716
|
+
} catch {
|
|
717
|
+
topSubdirs = [];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const allDeps = {
|
|
721
|
+
...(pkg?.dependencies || {}),
|
|
722
|
+
...(pkg?.devDependencies || {}),
|
|
723
|
+
};
|
|
724
|
+
for (const sp of subProjects) {
|
|
725
|
+
if (sp.pkgObj) {
|
|
726
|
+
Object.assign(
|
|
727
|
+
allDeps,
|
|
728
|
+
sp.pkgObj.dependencies || {},
|
|
729
|
+
sp.pkgObj.devDependencies || {}
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const stackSet = new Set();
|
|
735
|
+
if (allDeps['@angular/core']) stackSet.add('Angular 20+');
|
|
736
|
+
if (allDeps['@angular-architects/native-federation']) {
|
|
737
|
+
stackSet.add('Native Federation MFE');
|
|
738
|
+
}
|
|
739
|
+
if (allDeps.react) stackSet.add('React 19');
|
|
740
|
+
if (allDeps.vue) stackSet.add('Vue 3');
|
|
741
|
+
if (allDeps.electron) stackSet.add('Electron Desktop');
|
|
742
|
+
if (allDeps.three) stackSet.add('Three.js / WebGL');
|
|
743
|
+
if (allDeps.gsap) stackSet.add('GSAP Motion');
|
|
744
|
+
if (allDeps.vite) stackSet.add('Vite');
|
|
745
|
+
if (extCounts['.kt']) stackSet.add('Kotlin / Spring Boot');
|
|
746
|
+
if (extCounts['.java']) stackSet.add('Java / Spring Boot');
|
|
747
|
+
if (extCounts['.go']) stackSet.add('Go');
|
|
748
|
+
if (reqTxt || extCounts['.py']) stackSet.add('Python / FastAPI');
|
|
749
|
+
if (allDeps['@nestjs/core']) stackSet.add('NestJS');
|
|
750
|
+
if (allDeps.fastify) stackSet.add('Fastify');
|
|
751
|
+
if (allDeps.express) stackSet.add('Express / Node.js');
|
|
752
|
+
if (allDeps['discord.js']) stackSet.add('Discord.js');
|
|
753
|
+
if (allDeps.mongoose || allDeps.mongodb || extCounts['.kt']) {
|
|
754
|
+
if (repoName === 'JourneyLink') stackSet.add('MongoDB + RabbitMQ + Redis');
|
|
755
|
+
}
|
|
756
|
+
if (allDeps['@zen-ui/system'] || pkg?.name === '@zen-ui/system') {
|
|
757
|
+
stackSet.add('@zen-ui/system (CAPRI)');
|
|
758
|
+
}
|
|
759
|
+
if (extCounts['.ts'] || extCounts['.tsx']) stackSet.add('TypeScript');
|
|
760
|
+
if (extCounts['.ps1']) stackSet.add('PowerShell');
|
|
761
|
+
if (extCounts['.html'] && stackSet.size === 0) {
|
|
762
|
+
stackSet.add('Static HTML5 / CSS3 / JS');
|
|
763
|
+
}
|
|
764
|
+
if (hasDockerCompose || hasDockerfile) stackSet.add('Docker Containerized');
|
|
765
|
+
const stackTags = [...stackSet];
|
|
766
|
+
|
|
767
|
+
return {
|
|
768
|
+
absDir,
|
|
769
|
+
repoName,
|
|
770
|
+
pkg,
|
|
771
|
+
executiveSummary:
|
|
772
|
+
REPO_EXECUTIVE_SUMMARIES[repoName] ||
|
|
773
|
+
pkg?.description ||
|
|
774
|
+
`${repoName} software repository (${stackTags.join(', ')}).`,
|
|
775
|
+
hasDockerCompose,
|
|
776
|
+
hasDockerfile,
|
|
777
|
+
subProjects,
|
|
778
|
+
allFiles,
|
|
779
|
+
extCounts,
|
|
780
|
+
totalLoc,
|
|
781
|
+
topFiles,
|
|
782
|
+
topSubdirs,
|
|
783
|
+
stackTags,
|
|
784
|
+
exportedSymbols: [...exportedSymbols],
|
|
785
|
+
routesAndEndpoints: [...routesAndEndpoints],
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function generateStaticDocu(targetDir) {
|
|
790
|
+
const info = analyzeRepo(targetDir);
|
|
791
|
+
const {
|
|
792
|
+
repoName,
|
|
793
|
+
pkg,
|
|
794
|
+
executiveSummary,
|
|
795
|
+
hasDockerCompose,
|
|
796
|
+
hasDockerfile,
|
|
797
|
+
subProjects,
|
|
798
|
+
allFiles,
|
|
799
|
+
extCounts,
|
|
800
|
+
totalLoc,
|
|
801
|
+
topFiles,
|
|
802
|
+
topSubdirs,
|
|
803
|
+
stackTags,
|
|
804
|
+
exportedSymbols,
|
|
805
|
+
routesAndEndpoints,
|
|
806
|
+
} = info;
|
|
807
|
+
|
|
808
|
+
const extSummary = Object.entries(extCounts)
|
|
809
|
+
.sort((a, b) => b[1] - a[1])
|
|
810
|
+
.map(([ext, count]) => `\`${ext}\` (${count})`)
|
|
811
|
+
.join(', ');
|
|
812
|
+
|
|
813
|
+
const mermaidLines = ['flowchart TD'];
|
|
814
|
+
const safeId = (s, idx) => `N${idx}_${s.replace(/[^A-Za-z0-9]/g, '')}`;
|
|
815
|
+
mermaidLines.push(
|
|
816
|
+
` Repo["${repoName}${pkg?.version ? ` v${pkg.version}` : ''}"]`
|
|
817
|
+
);
|
|
818
|
+
mermaidLines.push(` Stack["Stack: ${stackTags.slice(0, 5).join(' · ')}"]`);
|
|
819
|
+
mermaidLines.push(' Repo --> Stack');
|
|
820
|
+
|
|
821
|
+
if (subProjects.length > 1) {
|
|
822
|
+
subProjects.slice(0, 12).forEach((sp, idx) => {
|
|
823
|
+
const nodeId = safeId(sp.relPath, idx);
|
|
824
|
+
mermaidLines.push(` ${nodeId}["${sp.relPath} (${sp.stack})"]`);
|
|
825
|
+
mermaidLines.push(` Stack --> ${nodeId}`);
|
|
826
|
+
});
|
|
827
|
+
} else if (topSubdirs.length > 0) {
|
|
828
|
+
topSubdirs.slice(0, 8).forEach((dir, idx) => {
|
|
829
|
+
const nodeId = safeId(dir, idx);
|
|
830
|
+
mermaidLines.push(` ${nodeId}["/${dir}"]`);
|
|
831
|
+
mermaidLines.push(` Stack --> ${nodeId}`);
|
|
832
|
+
});
|
|
833
|
+
} else {
|
|
834
|
+
topFiles.slice(0, 6).forEach((f, idx) => {
|
|
835
|
+
const nodeId = safeId(f.rel, idx);
|
|
836
|
+
mermaidLines.push(` ${nodeId}["${f.rel} (${f.lines}L)"]`);
|
|
837
|
+
mermaidLines.push(` Stack --> ${nodeId}`);
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (hasDockerCompose || hasDockerfile) {
|
|
842
|
+
mermaidLines.push(' Docker["Docker / Compose Runtime"]');
|
|
843
|
+
mermaidLines.push(' Repo -.->|"Containerized"| Docker');
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const subProjectsSection =
|
|
847
|
+
subProjects.length > 0
|
|
848
|
+
? [
|
|
849
|
+
'',
|
|
850
|
+
`## 3. Monorepo Sub-Projects & Microservices (${subProjects.length} Detected)`,
|
|
851
|
+
'| Sub-Project Path | Package / Service Name | Detected Tech Stack |',
|
|
852
|
+
'| :--- | :--- | :--- |',
|
|
853
|
+
...subProjects
|
|
854
|
+
.slice(0, 40)
|
|
855
|
+
.map(
|
|
856
|
+
(sp) => `| \`${sp.relPath}\` | \`${sp.name}\` | ${sp.stack} |`
|
|
857
|
+
),
|
|
858
|
+
'',
|
|
859
|
+
].join('\n')
|
|
860
|
+
: '';
|
|
861
|
+
|
|
862
|
+
const scriptsList = pkg?.scripts
|
|
863
|
+
? Object.entries(pkg.scripts)
|
|
864
|
+
.slice(0, 18)
|
|
865
|
+
.map(([k, v]) => `- \`npm run ${k}\`: \`${v.slice(0, 140)}${v.length > 140 ? '...' : ''}\``)
|
|
866
|
+
.join('\n')
|
|
867
|
+
: '- Standalone / Sub-project execution (`Dockerfile`, Maven/Gradle, Python, or sub-folder `package.json`)';
|
|
868
|
+
|
|
869
|
+
const topFilesTable = [
|
|
870
|
+
'| Module Path | Lines | Extension |',
|
|
871
|
+
'| :--- | ---: | :--- |',
|
|
872
|
+
...topFiles.map((f) => `| \`${f.rel}\` | ${f.lines} | \`${f.ext}\` |`),
|
|
873
|
+
].join('\n');
|
|
874
|
+
|
|
875
|
+
const symbolsPreview =
|
|
876
|
+
exportedSymbols.length > 0
|
|
877
|
+
? exportedSymbols.slice(0, 32).map((s) => `\`${s}\``).join(', ')
|
|
878
|
+
: 'Internal / Script-level entrypoints';
|
|
879
|
+
|
|
880
|
+
const routesSection =
|
|
881
|
+
routesAndEndpoints.length > 0
|
|
882
|
+
? `\n## 6. Detected HTTP / WebSocket / API Endpoints (${routesAndEndpoints.length})\n${routesAndEndpoints
|
|
883
|
+
.map((r) => `- \`${r}\``)
|
|
884
|
+
.join('\n')}\n`
|
|
885
|
+
: '';
|
|
886
|
+
|
|
887
|
+
return [
|
|
888
|
+
'---',
|
|
889
|
+
`project: "${repoName}"`,
|
|
890
|
+
`version: "${pkg?.version || '1.0.0'}"`,
|
|
891
|
+
`files: ${allFiles.length}`,
|
|
892
|
+
`loc: ${totalLoc}`,
|
|
893
|
+
'tags: [capri-docu, architecture, ideaprojects]',
|
|
894
|
+
'---',
|
|
895
|
+
`# ${repoName} // CAPRI Architecture & AI Context Dossier`,
|
|
896
|
+
`> **Executive Summary:** ${executiveSummary}`,
|
|
897
|
+
'>',
|
|
898
|
+
'> *Generated automatically via `npx capri docu` (Zero build or browser runtime overhead) · Linked to [[000-Master-Workspace-Index]]*',
|
|
899
|
+
'',
|
|
900
|
+
'## 1. Project Identity & Stack Overview',
|
|
901
|
+
`- **Repository:** \`${repoName}\`${pkg?.name ? ` (\`${pkg.name}\`)` : ''}`,
|
|
902
|
+
`- **Version:** \`${pkg?.version || '1.0.0'}\``,
|
|
903
|
+
`- **Detected Stack:** ${stackTags.join(' · ')}`,
|
|
904
|
+
`- **Top-Level Subsystems:** ${
|
|
905
|
+
topSubdirs.length > 0
|
|
906
|
+
? topSubdirs.map((d) => `\`/${d}\``).join(', ')
|
|
907
|
+
: 'Root-level flat topology'
|
|
908
|
+
}`,
|
|
909
|
+
`- **Container Readiness:** ${
|
|
910
|
+
hasDockerCompose
|
|
911
|
+
? '`docker-compose.yml` present'
|
|
912
|
+
: hasDockerfile
|
|
913
|
+
? '`Dockerfile` present'
|
|
914
|
+
: 'Host / Native runtime'
|
|
915
|
+
}`,
|
|
916
|
+
'',
|
|
917
|
+
'## 2. Codebase Metrics & Language Breakdown',
|
|
918
|
+
`- **Total Source Files Scanned:** ${allFiles.length}`,
|
|
919
|
+
`- **Total Lines of Code (LOC):** ${totalLoc.toLocaleString('en-US')}`,
|
|
920
|
+
`- **File Extensions:** ${extSummary || 'N/A'}`,
|
|
921
|
+
`- **Key Domain Symbols / Classes / Functions (${exportedSymbols.length}):** ${symbolsPreview}`,
|
|
922
|
+
subProjectsSection,
|
|
923
|
+
'## 4. Architecture & Subsystem Topology (`flowchart TD`)',
|
|
924
|
+
'```mermaid',
|
|
925
|
+
mermaidLines.join('\n'),
|
|
926
|
+
'```',
|
|
927
|
+
'',
|
|
928
|
+
'## 5. Key Domain Modules & Entrypoints Inventory',
|
|
929
|
+
topFilesTable,
|
|
930
|
+
routesSection,
|
|
931
|
+
'## 7. Available Build & Automation Scripts',
|
|
932
|
+
scriptsList,
|
|
933
|
+
'',
|
|
934
|
+
'## 8. Capri AI Agent Protocol (Claude / Codex / Gemini / Antigravity)',
|
|
935
|
+
'- **Subroutine `orion` (`CAPRI // UNLEASHED`):** Use `@zen-ui/system` primitives and `<ZenIcon>` (40 vector glyphs) for UI views; keep action labels concise (`1-3 words`).',
|
|
936
|
+
'- **Subroutine `architect` (`SCS // ARCHITECT`):** Enforce strict typing (zero `any`, `readonly T[]`), modular files (`< 900` lines), and `--zen-*` CSS tokens.',
|
|
937
|
+
'- **Subroutine `guard` (`CYBERPSYCHOSIS // GUARD`):** Sanitize untrusted inputs (`capri.sanitizeForPrompt()`), prevent XSS/secret leaks, bind dropdowns to `onMouseDown`, and ensure zero horizontal overflow (`320px` to `4K`).',
|
|
938
|
+
'- **Subroutine `zen` (`THEME // MATRIX`):** Maintain 4 Dark + 4 Light themes (`>= 4.5:1` AA contrast) and 60fps `--zen-ease-spring` tactile motion.',
|
|
939
|
+
'- **Subroutine `docu` (`CAPRI // AUTO-DOCU`):** Re-run `npm run capri -- docu --workspace "C:\\Users\\Lukas\\IdeaProjects" --write` whenever architectural boundaries or modules change.',
|
|
940
|
+
'',
|
|
941
|
+
].join('\n');
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function generateMasterObsidianVaultIndex(repoInfos) {
|
|
945
|
+
const totalFiles = repoInfos.reduce((acc, r) => acc + r.allFiles.length, 0);
|
|
946
|
+
const totalLoc = repoInfos.reduce((acc, r) => acc + r.totalLoc, 0);
|
|
947
|
+
|
|
948
|
+
const tableRows = repoInfos.map((r) => {
|
|
949
|
+
const link = `[[repos-index/${r.repoName}|${r.repoName}]]`;
|
|
950
|
+
const stackShort = r.stackTags.slice(0, 4).join(' · ');
|
|
951
|
+
return `| ${link} | ${r.allFiles.length} | ${r.totalLoc.toLocaleString(
|
|
952
|
+
'en-US'
|
|
953
|
+
)} | ${stackShort} | ${r.executiveSummary} |`;
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
const detailBlocks = repoInfos.map((r) => {
|
|
957
|
+
const subCount =
|
|
958
|
+
r.subProjects.length > 0
|
|
959
|
+
? ` (${r.subProjects.length} sub-projects/services)`
|
|
960
|
+
: '';
|
|
961
|
+
const topMods = r.topFiles
|
|
962
|
+
.slice(0, 4)
|
|
963
|
+
.map((f) => `\`${f.rel}\` (${f.lines}L)`)
|
|
964
|
+
.join(', ');
|
|
965
|
+
const endpointsPreview =
|
|
966
|
+
r.routesAndEndpoints.length > 0
|
|
967
|
+
? `\n- **Key Endpoints:** ${r.routesAndEndpoints
|
|
968
|
+
.slice(0, 8)
|
|
969
|
+
.map((e) => `\`${e}\``)
|
|
970
|
+
.join(', ')}`
|
|
971
|
+
: '';
|
|
972
|
+
return [
|
|
973
|
+
`### [[repos-index/${r.repoName}|${r.repoName}]]${subCount}`,
|
|
974
|
+
`- **Summary:** ${r.executiveSummary}`,
|
|
975
|
+
`- **Stack:** ${r.stackTags.join(' · ')}`,
|
|
976
|
+
`- **Scale:** ${r.allFiles.length} files · ${r.totalLoc.toLocaleString(
|
|
977
|
+
'en-US'
|
|
978
|
+
)} LOC (${Object.entries(r.extCounts)
|
|
979
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
980
|
+
.join(', ')})`,
|
|
981
|
+
`- **Core Entrypoints:** ${topMods || 'N/A'}${endpointsPreview}`,
|
|
982
|
+
].join('\n');
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
return [
|
|
986
|
+
'---',
|
|
987
|
+
'title: "Capri Master Workspace & Repository Index (IdeaProjects)"',
|
|
988
|
+
'tags: [capri-vault, moc, workspace-index, architecture]',
|
|
989
|
+
'---',
|
|
990
|
+
'# 000 — Capri Master Workspace & Repository Index (`IdeaProjects`)',
|
|
991
|
+
'> **Instant AI Context Map for Capri Personal Assistant & Coding Agents**',
|
|
992
|
+
`> Covers **${repoInfos.length} repositories**, **${totalFiles.toLocaleString(
|
|
993
|
+
'en-US'
|
|
994
|
+
)} source files**, and **${totalLoc.toLocaleString(
|
|
995
|
+
'en-US'
|
|
996
|
+
)} lines of code** across \`C:\\Users\\Lukas\\IdeaProjects\\\`.`,
|
|
997
|
+
'> When answering questions about any project below, read this index first (and the linked `[[repos-index/<Repo>]]` note if deeper file/endpoint detail is needed) — no full-disk repo crawling required.',
|
|
998
|
+
'',
|
|
999
|
+
'## 1. Live Production & Hetzner Deployment Topology (`2.31.32.227`)',
|
|
1000
|
+
'- **`https://lpabst.dev` (`portfolio-web`):** Creative 3D Portfolio (`portfolio` repo · React 19 + Three.js + `@zen-ui/system`).',
|
|
1001
|
+
'- **`https://ui.lpabst.dev` (`zen-ui-web`):** `@zen-ui/system // CAPRI v1.1.0` Design System Showcase & 51-Module Storybook (`zeN-UI` repo).',
|
|
1002
|
+
'- **`https://terminal.lpabst.dev` (`zen-terminal-web`):** `zeN-Terminal` 3D Cyber-Shell Showcase (`zeN-Terminal` repo · also installed locally across Windows Terminal, PowerShell `$PROFILE`, and `cmd.exe`).',
|
|
1003
|
+
'- **Reverse Proxy & Hardening:** `caddy-gateway` (`80/443`) routes to isolated internal Docker bridge networks (`portfolio-net`, `zen-ui-net`, `zen-terminal-net`). All web containers run rootless (`101:101`), `read_only: true`, `cap_drop: ALL`, `256M` RAM limit.',
|
|
1004
|
+
'',
|
|
1005
|
+
'## 2. Master Repository Matrix (`C:\\Users\\Lukas\\IdeaProjects`)',
|
|
1006
|
+
'| Repository Note | Files | LOC | Primary Tech Stack | Executive Purpose |',
|
|
1007
|
+
'| :--- | ---: | ---: | :--- | :--- |',
|
|
1008
|
+
...tableRows,
|
|
1009
|
+
'',
|
|
1010
|
+
'## 3. Per-Repository Instant Context Briefs',
|
|
1011
|
+
...detailBlocks,
|
|
1012
|
+
'',
|
|
1013
|
+
'## 4. Mandatory Auto-Docu Synchronization Rule',
|
|
1014
|
+
'Whenever any repository in `C:\\Users\\Lukas\\IdeaProjects` adds/removes modules, changes APIs, or updates dependencies, run:',
|
|
1015
|
+
'```powershell',
|
|
1016
|
+
'npm --prefix "C:\\Users\\Lukas\\IdeaProjects\\zeN-UI" run capri -- docu --workspace "C:\\Users\\Lukas\\IdeaProjects" --write',
|
|
1017
|
+
'```',
|
|
1018
|
+
'This single command regenerates every repo\'s `docs/CAPRI-ARCHITECTURE.md` AND updates `capri-vault/000-Master-Workspace-Index.md` + `capri-vault/repos-index/*.md` in `< 2 seconds`.',
|
|
1019
|
+
'',
|
|
1020
|
+
].join('\n');
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const [, , rawCommand = 'help', rawArg = 'orion'] = process.argv;
|
|
1024
|
+
const command = rawCommand.toLowerCase();
|
|
1025
|
+
const voiceEnabled = process.argv.includes('--voice');
|
|
1026
|
+
const writeEnabled = process.argv.includes('--write');
|
|
1027
|
+
const workspaceIdx = process.argv.indexOf('--workspace');
|
|
1028
|
+
const workspaceDir =
|
|
1029
|
+
workspaceIdx !== -1 && process.argv[workspaceIdx + 1]
|
|
1030
|
+
? path.resolve(process.argv[workspaceIdx + 1])
|
|
1031
|
+
: null;
|
|
1032
|
+
const rootDir = process.cwd();
|
|
1033
|
+
|
|
1034
|
+
if (command === 'docu') {
|
|
1035
|
+
if (workspaceDir && fs.existsSync(workspaceDir)) {
|
|
1036
|
+
const entries = fs
|
|
1037
|
+
.readdirSync(workspaceDir, { withFileTypes: true })
|
|
1038
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith('.'));
|
|
1039
|
+
const summaryRows = [];
|
|
1040
|
+
const repoInfos = [];
|
|
1041
|
+
const capriVaultDir = path.join(
|
|
1042
|
+
workspaceDir,
|
|
1043
|
+
'Capri-Assistant',
|
|
1044
|
+
'capri-vault'
|
|
1045
|
+
);
|
|
1046
|
+
const capriReposIndexDir = path.join(capriVaultDir, 'repos-index');
|
|
1047
|
+
if (writeEnabled && fs.existsSync(path.join(workspaceDir, 'Capri-Assistant'))) {
|
|
1048
|
+
fs.mkdirSync(capriReposIndexDir, { recursive: true });
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
for (const entry of entries) {
|
|
1052
|
+
const repoPath = path.join(workspaceDir, entry.name);
|
|
1053
|
+
const info = analyzeRepo(repoPath);
|
|
1054
|
+
repoInfos.push(info);
|
|
1055
|
+
const docContent = generateStaticDocu(repoPath);
|
|
1056
|
+
const outDir = path.join(repoPath, 'docs');
|
|
1057
|
+
const outFile = path.join(outDir, 'CAPRI-ARCHITECTURE.md');
|
|
1058
|
+
if (writeEnabled) {
|
|
1059
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
1060
|
+
fs.writeFileSync(outFile, docContent, 'utf8');
|
|
1061
|
+
if (fs.existsSync(capriReposIndexDir)) {
|
|
1062
|
+
fs.writeFileSync(
|
|
1063
|
+
path.join(capriReposIndexDir, `${entry.name}.md`),
|
|
1064
|
+
docContent,
|
|
1065
|
+
'utf8'
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
summaryRows.push(
|
|
1070
|
+
`- **${entry.name}**: \`${outFile}\` (${info.allFiles.length} source files · ${info.totalLoc.toLocaleString('en-US')} LOC · ${info.subProjects.length} sub-projects)`
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (writeEnabled && fs.existsSync(capriVaultDir)) {
|
|
1075
|
+
const masterIndexContent = generateMasterObsidianVaultIndex(repoInfos);
|
|
1076
|
+
const masterIndexPath = path.join(
|
|
1077
|
+
capriVaultDir,
|
|
1078
|
+
'000-Master-Workspace-Index.md'
|
|
1079
|
+
);
|
|
1080
|
+
fs.writeFileSync(masterIndexPath, masterIndexContent, 'utf8');
|
|
1081
|
+
summaryRows.push(
|
|
1082
|
+
`- **Master Obsidian Vault Index**: \`${masterIndexPath}\` (Synced ${repoInfos.length} repos into \`capri-vault/repos-index/\`)`
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
console.log(
|
|
1087
|
+
`# CAPRI // AUTO-DOCU — Workspace & Obsidian Vault Sync Complete (${entries.length} Repositories)\n`
|
|
1088
|
+
);
|
|
1089
|
+
console.log(summaryRows.join('\n'));
|
|
1090
|
+
} else {
|
|
1091
|
+
const targetPath =
|
|
1092
|
+
rawArg && !rawArg.startsWith('--') && rawArg !== 'orion'
|
|
1093
|
+
? path.resolve(rawArg)
|
|
1094
|
+
: rootDir;
|
|
1095
|
+
const docContent = generateStaticDocu(targetPath);
|
|
1096
|
+
if (writeEnabled) {
|
|
1097
|
+
const outDir = path.join(targetPath, 'docs');
|
|
1098
|
+
const outFile = path.join(outDir, 'CAPRI-ARCHITECTURE.md');
|
|
1099
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
1100
|
+
fs.writeFileSync(outFile, docContent, 'utf8');
|
|
1101
|
+
console.log(`Wrote Capri Architecture Dossier to: ${outFile}`);
|
|
1102
|
+
} else {
|
|
1103
|
+
console.log(docContent);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
} else if (command === 'orion') {
|
|
1107
|
+
runOrionAudit(rootDir, voiceEnabled);
|
|
1108
|
+
} else if (command === 'architect') {
|
|
1109
|
+
runArchitectAudit(rootDir, voiceEnabled);
|
|
1110
|
+
} else if (command === 'guard' || command === 'sentinel') {
|
|
1111
|
+
runGuardAudit(rootDir, voiceEnabled);
|
|
1112
|
+
} else if (command === 'zen') {
|
|
1113
|
+
runZenAudit(rootDir, voiceEnabled);
|
|
1114
|
+
} else if (command === 'all') {
|
|
1115
|
+
runOrionAudit(rootDir, voiceEnabled);
|
|
1116
|
+
console.log('\n---\n');
|
|
1117
|
+
runArchitectAudit(rootDir, voiceEnabled);
|
|
1118
|
+
console.log('\n---\n');
|
|
1119
|
+
runGuardAudit(rootDir, voiceEnabled);
|
|
1120
|
+
console.log('\n---\n');
|
|
1121
|
+
runZenAudit(rootDir, voiceEnabled);
|
|
1122
|
+
} else if (command === 'scan') {
|
|
1123
|
+
const inputPayload = process.argv
|
|
1124
|
+
.slice(3)
|
|
1125
|
+
.filter((a) => a !== '--voice')
|
|
1126
|
+
.join(' ');
|
|
1127
|
+
const sub = SUBROUTINES.sentinel;
|
|
1128
|
+
printHeader('guard // scan', sub, voiceEnabled);
|
|
1129
|
+
const res = scanTextForPromptInjection(inputPayload);
|
|
1130
|
+
console.log(`## Prompt-Injection & Security Payload Scan`);
|
|
1131
|
+
console.log(`- Safe: ${res.safe ? 'TRUE (CLEAN)' : 'FALSE (BLOCKED)'}`);
|
|
1132
|
+
console.log(`- Threats Detected: ${res.threats.length}`);
|
|
1133
|
+
for (const t of res.threats) {
|
|
1134
|
+
console.log(` * [${t.category}] ${t.patternName}: "${t.snippet}"`);
|
|
1135
|
+
}
|
|
1136
|
+
console.log(`- Sanitized Output: ${res.sanitized}`);
|
|
1137
|
+
} else if (command === 'pack-npm' || command === 'publish-npm') {
|
|
1138
|
+
const pkgPath = path.join(rootDir, 'package.json');
|
|
1139
|
+
const originalContent = fs.readFileSync(pkgPath, 'utf8');
|
|
1140
|
+
try {
|
|
1141
|
+
if (command === 'publish-npm') {
|
|
1142
|
+
console.log('Running Capri all-subroutine audit & production build...');
|
|
1143
|
+
execSync('npm run capri -- all && npm run build', {
|
|
1144
|
+
cwd: rootDir,
|
|
1145
|
+
stdio: 'inherit',
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
const pkg = JSON.parse(originalContent);
|
|
1149
|
+
pkg.name = '@zen-ui-capri/system';
|
|
1150
|
+
pkg.publishConfig = {
|
|
1151
|
+
registry: 'https://registry.npmjs.org',
|
|
1152
|
+
access: 'public',
|
|
1153
|
+
};
|
|
1154
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
1155
|
+
if (command === 'pack-npm') {
|
|
1156
|
+
execSync('npm pack --ignore-scripts', {
|
|
1157
|
+
cwd: rootDir,
|
|
1158
|
+
stdio: 'inherit',
|
|
1159
|
+
});
|
|
1160
|
+
} else {
|
|
1161
|
+
console.log('Publishing @zen-ui-capri/system to https://registry.npmjs.org...');
|
|
1162
|
+
execSync('npm publish --access public --ignore-scripts --auth-type=web', {
|
|
1163
|
+
cwd: rootDir,
|
|
1164
|
+
stdio: 'inherit',
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
} finally {
|
|
1168
|
+
fs.writeFileSync(pkgPath, originalContent, 'utf8');
|
|
1169
|
+
}
|
|
1170
|
+
} else if (command === 'skill') {
|
|
1171
|
+
const rawKey = rawArg.toLowerCase();
|
|
1172
|
+
const key = rawKey === 'guard' ? 'sentinel' : rawKey;
|
|
1173
|
+
const sub = SUBROUTINES[key] || SUBROUTINES.orion;
|
|
1174
|
+
printHeader(rawKey, sub, voiceEnabled);
|
|
1175
|
+
sub.rules.forEach((r, i) => console.log(`${i + 1}. ${r}`));
|
|
1176
|
+
} else {
|
|
1177
|
+
console.log(`@zen-ui/system // CAPRI AI Co-Pilot CLI (On-Demand · Zero Build Overhead)
|
|
1178
|
+
Usage:
|
|
1179
|
+
npm run capri -- docu Generate static architecture, component & user-flow Markdown
|
|
1180
|
+
npm run capri -- orion [--voice] Run UI & Component Synthesis audit (51 modules, 40 icons)
|
|
1181
|
+
npm run capri -- architect [--voice] Run TypeScript & CSS Token Governance audit (zero any, zero hex)
|
|
1182
|
+
npm run capri -- guard [--voice] Run Security, Anti-Prompt-Injection, WCAG & Overflow Guard audit
|
|
1183
|
+
npm run capri -- zen [--voice] Run 8-Theme Polarity & 60fps Motion audit
|
|
1184
|
+
npm run capri -- all [--voice] Run all 4 audit subroutines (orion, architect, guard, zen)
|
|
1185
|
+
npm run capri -- scan "<text>" [--voice] Scan and sanitize untrusted text for prompt injection / XSS
|
|
1186
|
+
npm run capri -- skill <subroutine> [--voice] Print ruleset for: guard | orion | architect | sentinel | zen | docu`);
|
|
1187
|
+
}
|