@lifeonlars/prime-yggdrasil 0.2.6 â 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai/agents/accessibility.md +588 -0
- package/.ai/agents/block-composer.md +909 -0
- package/.ai/agents/drift-validator.md +784 -0
- package/.ai/agents/interaction-patterns.md +473 -0
- package/.ai/agents/primeflex-guard.md +815 -0
- package/.ai/agents/semantic-token-intent.md +739 -0
- package/README.md +138 -12
- package/cli/bin/yggdrasil.js +134 -0
- package/cli/commands/audit.js +447 -0
- package/cli/commands/init.js +288 -0
- package/cli/commands/validate.js +433 -0
- package/cli/rules/accessibility/index.js +15 -0
- package/cli/rules/accessibility/missing-alt-text.js +201 -0
- package/cli/rules/accessibility/missing-form-labels.js +238 -0
- package/cli/rules/interaction-patterns/focus-management.js +187 -0
- package/cli/rules/interaction-patterns/generic-copy.js +190 -0
- package/cli/rules/interaction-patterns/index.js +17 -0
- package/cli/rules/interaction-patterns/state-completeness.js +194 -0
- package/cli/templates/.ai/yggdrasil/README.md +308 -0
- package/docs/AESTHETICS.md +168 -0
- package/docs/PHASE-6-PLAN.md +456 -0
- package/docs/PRIMEFLEX-POLICY.md +737 -0
- package/package.json +6 -1
- package/docs/Fixes.md +0 -258
- package/docs/archive/README.md +0 -27
- package/docs/archive/SEMANTIC-MIGRATION-PLAN.md +0 -177
- package/docs/archive/YGGDRASIL_THEME.md +0 -264
- package/docs/archive/agentic_policy.md +0 -216
- package/docs/contrast-report.md +0 -9
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function question(query) {
|
|
15
|
+
return new Promise((resolve) => rl.question(query, resolve));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function initCommand() {
|
|
19
|
+
console.log(`
|
|
20
|
+
đŗ Yggdrasil Agent Infrastructure Setup
|
|
21
|
+
ââââââââââââââââââââââââââââââââââââââââââââ
|
|
22
|
+
|
|
23
|
+
This will set up AI agents in your project to guide design
|
|
24
|
+
system usage and prevent drift.
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Detect project type
|
|
29
|
+
const projectType = detectProjectType();
|
|
30
|
+
console.log(`đĻ Detected project type: ${projectType}`);
|
|
31
|
+
console.log('');
|
|
32
|
+
|
|
33
|
+
// Ask user preferences
|
|
34
|
+
const copyAgents = await askYesNo('Copy agent documentation to .ai/yggdrasil/? (Recommended)', true);
|
|
35
|
+
const installESLint = await askYesNo('Add ESLint config reference? (Available in Phase 3)', false);
|
|
36
|
+
const addScripts = await askYesNo('Add npm scripts to package.json?', true);
|
|
37
|
+
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log('đ Installation Plan:');
|
|
40
|
+
console.log('');
|
|
41
|
+
if (copyAgents) console.log(' â Copy 4 agent specifications to .ai/yggdrasil/');
|
|
42
|
+
if (installESLint) console.log(' â Add ESLint configuration (placeholder for Phase 3)');
|
|
43
|
+
if (addScripts) console.log(' â Add npm scripts for validation');
|
|
44
|
+
console.log('');
|
|
45
|
+
|
|
46
|
+
const proceed = await askYesNo('Proceed with installation?', true);
|
|
47
|
+
if (!proceed) {
|
|
48
|
+
console.log('â Installation cancelled.');
|
|
49
|
+
rl.close();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log('đ Installing...');
|
|
55
|
+
console.log('');
|
|
56
|
+
|
|
57
|
+
// Copy agents
|
|
58
|
+
if (copyAgents) {
|
|
59
|
+
await copyAgentFiles();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Add ESLint config
|
|
63
|
+
if (installESLint) {
|
|
64
|
+
await createESLintConfig();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add scripts
|
|
68
|
+
if (addScripts) {
|
|
69
|
+
await addNpmScripts();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Print success message
|
|
73
|
+
printSuccessMessage(copyAgents, installESLint);
|
|
74
|
+
|
|
75
|
+
rl.close();
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('â Error during installation:', error.message);
|
|
78
|
+
rl.close();
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function detectProjectType() {
|
|
84
|
+
const cwd = process.cwd();
|
|
85
|
+
|
|
86
|
+
if (existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.mjs'))) {
|
|
87
|
+
return 'Next.js';
|
|
88
|
+
}
|
|
89
|
+
if (existsSync(join(cwd, 'vite.config.js')) || existsSync(join(cwd, 'vite.config.ts'))) {
|
|
90
|
+
return 'Vite';
|
|
91
|
+
}
|
|
92
|
+
if (existsSync(join(cwd, 'package.json'))) {
|
|
93
|
+
return 'React/Node.js';
|
|
94
|
+
}
|
|
95
|
+
return 'Unknown';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function askYesNo(question, defaultYes = true) {
|
|
99
|
+
const prompt = `${question} ${defaultYes ? '[Y/n]' : '[y/N]'}: `;
|
|
100
|
+
const answer = await questionPromise(prompt);
|
|
101
|
+
const normalized = answer.toLowerCase().trim();
|
|
102
|
+
|
|
103
|
+
if (normalized === '') return defaultYes;
|
|
104
|
+
return normalized === 'y' || normalized === 'yes';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function questionPromise(query) {
|
|
108
|
+
return new Promise((resolve) => rl.question(query, resolve));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function copyAgentFiles() {
|
|
112
|
+
const cwd = process.cwd();
|
|
113
|
+
const targetDir = join(cwd, '.ai', 'yggdrasil');
|
|
114
|
+
const sourceDir = join(__dirname, '../templates/.ai/yggdrasil');
|
|
115
|
+
|
|
116
|
+
// Create target directory
|
|
117
|
+
if (!existsSync(targetDir)) {
|
|
118
|
+
mkdirSync(targetDir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Copy agent files from main .ai/agents directory
|
|
122
|
+
const mainAgentsDir = join(__dirname, '../../.ai/agents');
|
|
123
|
+
const agentFiles = [
|
|
124
|
+
'block-composer.md',
|
|
125
|
+
'primeflex-guard.md',
|
|
126
|
+
'semantic-token-intent.md',
|
|
127
|
+
'drift-validator.md'
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
console.log(' đ Copying agent specifications...');
|
|
131
|
+
for (const file of agentFiles) {
|
|
132
|
+
const source = join(mainAgentsDir, file);
|
|
133
|
+
const target = join(targetDir, file);
|
|
134
|
+
|
|
135
|
+
if (existsSync(source)) {
|
|
136
|
+
copyFileSync(source, target);
|
|
137
|
+
console.log(` â ${file}`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(` â ī¸ ${file} not found (will be available after build)`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Copy README
|
|
144
|
+
const readmePath = join(sourceDir, 'README.md');
|
|
145
|
+
if (existsSync(readmePath)) {
|
|
146
|
+
copyFileSync(readmePath, join(targetDir, 'README.md'));
|
|
147
|
+
console.log(' â README.md');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Copy PrimeFlex policy
|
|
151
|
+
const policySource = join(__dirname, '../../docs/PRIMEFLEX-POLICY.md');
|
|
152
|
+
if (existsSync(policySource)) {
|
|
153
|
+
copyFileSync(policySource, join(targetDir, 'PRIMEFLEX-POLICY.md'));
|
|
154
|
+
console.log(' â PRIMEFLEX-POLICY.md');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(` â
Copied agents to ${targetDir}`);
|
|
159
|
+
console.log('');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function createESLintConfig() {
|
|
163
|
+
const cwd = process.cwd();
|
|
164
|
+
const configPath = join(cwd, '.eslintrc.yggdrasil.js');
|
|
165
|
+
|
|
166
|
+
const configContent = `// Yggdrasil ESLint Configuration
|
|
167
|
+
// This will be functional in Phase 3 when the ESLint plugin is published
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
extends: [
|
|
171
|
+
// Uncomment when @lifeonlars/eslint-plugin-yggdrasil is available (Phase 3)
|
|
172
|
+
// 'plugin:@lifeonlars/yggdrasil/recommended'
|
|
173
|
+
],
|
|
174
|
+
rules: {
|
|
175
|
+
// Phase 3: ESLint rules will enforce:
|
|
176
|
+
// - No hardcoded colors (use semantic tokens)
|
|
177
|
+
// - No PrimeFlex on PrimeReact components
|
|
178
|
+
// - PrimeFlex allowlist (layout/spacing only)
|
|
179
|
+
// - No Tailwind classes
|
|
180
|
+
// - Valid spacing (4px grid)
|
|
181
|
+
// - Semantic tokens only
|
|
182
|
+
// - Consistent PrimeReact imports
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
writeFileSync(configPath, configContent);
|
|
188
|
+
console.log(` â
Created ${configPath}`);
|
|
189
|
+
console.log(' â ī¸ ESLint plugin will be available in Phase 3');
|
|
190
|
+
console.log('');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function addNpmScripts() {
|
|
194
|
+
const cwd = process.cwd();
|
|
195
|
+
const packageJsonPath = join(cwd, 'package.json');
|
|
196
|
+
|
|
197
|
+
if (!existsSync(packageJsonPath)) {
|
|
198
|
+
console.log(' â ī¸ package.json not found, skipping script installation');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
203
|
+
|
|
204
|
+
if (!packageJson.scripts) {
|
|
205
|
+
packageJson.scripts = {};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Add scripts (placeholders for Phase 4)
|
|
209
|
+
const scriptsToAdd = {
|
|
210
|
+
'yggdrasil:validate': 'echo "â ī¸ Validation command available in Phase 4. Use ESLint for now."',
|
|
211
|
+
'yggdrasil:audit': 'echo "â ī¸ Audit command available in Phase 4. Use ESLint for now."'
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
let added = false;
|
|
215
|
+
for (const [key, value] of Object.entries(scriptsToAdd)) {
|
|
216
|
+
if (!packageJson.scripts[key]) {
|
|
217
|
+
packageJson.scripts[key] = value;
|
|
218
|
+
console.log(` â Added script: ${key}`);
|
|
219
|
+
added = true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (added) {
|
|
224
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log(' â
Updated package.json');
|
|
227
|
+
} else {
|
|
228
|
+
console.log(' âšī¸ Scripts already exist, skipping');
|
|
229
|
+
}
|
|
230
|
+
console.log('');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function printSuccessMessage(copiedAgents, addedESLint) {
|
|
234
|
+
console.log('');
|
|
235
|
+
console.log('ââââââââââââââââââââââââââââââââââââââââââââ');
|
|
236
|
+
console.log('â
Yggdrasil Agent Infrastructure Installed!');
|
|
237
|
+
console.log('ââââââââââââââââââââââââââââââââââââââââââââ');
|
|
238
|
+
console.log('');
|
|
239
|
+
|
|
240
|
+
if (copiedAgents) {
|
|
241
|
+
console.log('đ Agent Documentation:');
|
|
242
|
+
console.log(' .ai/yggdrasil/block-composer.md - Composition planning');
|
|
243
|
+
console.log(' .ai/yggdrasil/primeflex-guard.md - Layout constraints');
|
|
244
|
+
console.log(' .ai/yggdrasil/semantic-token-intent.md - Token selection');
|
|
245
|
+
console.log(' .ai/yggdrasil/drift-validator.md - Policy enforcement');
|
|
246
|
+
console.log(' .ai/yggdrasil/PRIMEFLEX-POLICY.md - PrimeFlex usage rules');
|
|
247
|
+
console.log('');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log('đ¤ Using Agents with AI Tools:');
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log(' Claude Code:');
|
|
253
|
+
console.log(' "Before implementing UI, read .ai/yggdrasil/block-composer.md');
|
|
254
|
+
console.log(' and suggest the appropriate PrimeReact components."');
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(' Cursor/Copilot:');
|
|
257
|
+
console.log(' Add .ai/yggdrasil/ to your AI context, then ask:');
|
|
258
|
+
console.log(' "Help me implement a user profile form following Yggdrasil agents."');
|
|
259
|
+
console.log('');
|
|
260
|
+
|
|
261
|
+
console.log('đ Next Steps:');
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(' 1. Read the agent documentation in .ai/yggdrasil/');
|
|
264
|
+
console.log(' 2. Reference agents when implementing UI features');
|
|
265
|
+
console.log(' 3. Phase 3: Install ESLint plugin for code-time validation');
|
|
266
|
+
console.log(' npm install --save-dev @lifeonlars/eslint-plugin-yggdrasil');
|
|
267
|
+
console.log(' 4. Phase 4: Use validation commands');
|
|
268
|
+
console.log(' npm run yggdrasil:validate');
|
|
269
|
+
console.log(' npm run yggdrasil:audit');
|
|
270
|
+
console.log('');
|
|
271
|
+
|
|
272
|
+
console.log('đ Agent Workflow:');
|
|
273
|
+
console.log('');
|
|
274
|
+
console.log(' Planning UI: Block Composer Agent');
|
|
275
|
+
console.log(' Choosing layout: PrimeFlex Guard Agent');
|
|
276
|
+
console.log(' Selecting colors: Semantic Token Intent Agent');
|
|
277
|
+
console.log(' Validating code: Drift Validator Agent');
|
|
278
|
+
console.log('');
|
|
279
|
+
|
|
280
|
+
console.log('đ Resources:');
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log(' Documentation: https://github.com/lifeonlars/prime-yggdrasil');
|
|
283
|
+
console.log(' PrimeReact: https://primereact.org/');
|
|
284
|
+
console.log(' PrimeFlex: https://primeflex.org/');
|
|
285
|
+
console.log('');
|
|
286
|
+
console.log('ââââââââââââââââââââââââââââââââââââââââââââ');
|
|
287
|
+
console.log('');
|
|
288
|
+
}
|