@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.
@@ -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
+ }