multimodel-dev-os 0.3.0 → 0.5.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/README.md +103 -98
- package/bin/multimodel-dev-os.js +420 -35
- package/docs/caveman-mode.md +7 -3
- package/docs/cli-roadmap.md +33 -21
- package/docs/comparison.md +33 -0
- package/docs/faq.md +21 -4
- package/docs/installers.md +1 -1
- package/docs/launch-kit.md +99 -0
- package/docs/multimodel-workflow.md +10 -11
- package/docs/npm-publishing.md +2 -2
- package/docs/quickstart.md +42 -29
- package/docs/templates-guide.md +65 -0
- package/docs/use-cases.md +39 -0
- package/examples/ecommerce-store/.ai/config.yaml +7 -4
- package/examples/ecommerce-store/.ai/context/architecture.md +6 -0
- package/examples/ecommerce-store/.ai/context/context-budget.md +4 -0
- package/examples/ecommerce-store/.ai/context/model-map.md +4 -0
- package/examples/ecommerce-store/.ai/context/project-brief.md +3 -0
- package/examples/ecommerce-store/.ai/skills/webhook-handler.md +15 -0
- package/examples/ecommerce-store/AGENTS.md +15 -2
- package/examples/ecommerce-store/MEMORY.md +4 -3
- package/examples/ecommerce-store/TASKS.md +10 -0
- package/examples/general-app/.ai/config.yaml +7 -4
- package/examples/general-app/.ai/context/architecture.md +12 -0
- package/examples/general-app/.ai/context/context-budget.md +5 -0
- package/examples/general-app/.ai/context/model-map.md +7 -0
- package/examples/general-app/.ai/context/project-brief.md +6 -0
- package/examples/general-app/.ai/skills/example-skill.md +8 -0
- package/examples/general-app/AGENTS.md +69 -4
- package/examples/general-app/MEMORY.md +32 -2
- package/examples/general-app/TASKS.md +9 -0
- package/examples/nextjs-saas/.ai/config.yaml +7 -4
- package/examples/nextjs-saas/.ai/context/architecture.md +8 -0
- package/examples/nextjs-saas/.ai/context/context-budget.md +4 -0
- package/examples/nextjs-saas/.ai/context/model-map.md +4 -0
- package/examples/nextjs-saas/.ai/context/project-brief.md +5 -0
- package/examples/nextjs-saas/.ai/skills/nextjs-action-build.md +16 -0
- package/examples/nextjs-saas/AGENTS.md +8 -2
- package/examples/nextjs-saas/MEMORY.md +7 -4
- package/examples/nextjs-saas/TASKS.md +12 -0
- package/examples/seo-landing-page/.ai/config.yaml +7 -4
- package/examples/seo-landing-page/.ai/context/architecture.md +12 -0
- package/examples/seo-landing-page/.ai/context/context-budget.md +5 -0
- package/examples/seo-landing-page/.ai/context/model-map.md +7 -0
- package/examples/seo-landing-page/.ai/context/project-brief.md +6 -0
- package/examples/seo-landing-page/.ai/skills/seo-audit.md +21 -0
- package/examples/seo-landing-page/AGENTS.md +69 -4
- package/examples/seo-landing-page/MEMORY.md +33 -4
- package/examples/seo-landing-page/TASKS.md +9 -0
- package/examples/wordpress-site/.ai/config.yaml +7 -4
- package/examples/wordpress-site/.ai/context/architecture.md +7 -0
- package/examples/wordpress-site/.ai/context/context-budget.md +4 -0
- package/examples/wordpress-site/.ai/context/model-map.md +4 -0
- package/examples/wordpress-site/.ai/context/project-brief.md +3 -0
- package/examples/wordpress-site/.ai/skills/plugin-boilerplate.md +21 -0
- package/examples/wordpress-site/AGENTS.md +16 -3
- package/examples/wordpress-site/MEMORY.md +4 -3
- package/examples/wordpress-site/TASKS.md +10 -0
- package/package.json +3 -2
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/verify.js +274 -0
- package/scripts/verify.sh +12 -12
package/bin/multimodel-dev-os.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* multimodel-dev-os CLI
|
|
5
|
-
* Dependency-free local initialization and validation utility.
|
|
5
|
+
* Dependency-free local initialization, diagnostics, and validation utility.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
@@ -13,7 +13,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
13
13
|
const __dirname = dirname(__filename);
|
|
14
14
|
const sourceRoot = resolve(__dirname, '..');
|
|
15
15
|
|
|
16
|
-
let version = '0.
|
|
16
|
+
let version = '0.5.1';
|
|
17
17
|
try {
|
|
18
18
|
const pkgData = JSON.parse(readFileSync(resolve(sourceRoot, 'package.json'), 'utf8'));
|
|
19
19
|
version = pkgData.version;
|
|
@@ -60,6 +60,44 @@ function parseArgs(args) {
|
|
|
60
60
|
const params = parseArgs(ARGS);
|
|
61
61
|
const COMMAND = params.command;
|
|
62
62
|
|
|
63
|
+
const TEMPLATES = {
|
|
64
|
+
'nextjs-saas': {
|
|
65
|
+
name: 'nextjs-saas',
|
|
66
|
+
description: 'Next.js App Router starter with TypeScript, Prisma database, Tailwind CSS, and Stripe subscription setup.',
|
|
67
|
+
stack: 'Next.js 14, React 18, TypeScript, Tailwind CSS, Prisma ORM, Stripe payments',
|
|
68
|
+
skill: 'nextjs-action-build.md',
|
|
69
|
+
skillDesc: 'React Server Actions secure implementation conventions.'
|
|
70
|
+
},
|
|
71
|
+
'wordpress-site': {
|
|
72
|
+
name: 'wordpress-site',
|
|
73
|
+
description: 'WordPress custom block theme and plugin development profile with secure PHP database query rules.',
|
|
74
|
+
stack: 'WordPress Core, PHP, Gutenberg Block APIs, theme customization hooks',
|
|
75
|
+
skill: 'plugin-boilerplate.md',
|
|
76
|
+
skillDesc: 'PHP hook registrations and sanitization gates boilerplate.'
|
|
77
|
+
},
|
|
78
|
+
'ecommerce-store': {
|
|
79
|
+
name: 'ecommerce-store',
|
|
80
|
+
description: 'PCI-compliant headless e-commerce store with secure checkout loops, card state validations, and Stripe webhooks.',
|
|
81
|
+
stack: 'Headless Store API, cart states, secure payment webhooks, order database triggers',
|
|
82
|
+
skill: 'webhook-handler.md',
|
|
83
|
+
skillDesc: 'Stripe order checkout webhook secure listener verification rules.'
|
|
84
|
+
},
|
|
85
|
+
'seo-landing-page': {
|
|
86
|
+
name: 'seo-landing-page',
|
|
87
|
+
description: 'Ultra-fast static landing page layout optimized for Astro, high Core Web Vitals scores, and JSON-LD schema markup.',
|
|
88
|
+
stack: 'Astro, HTML5, structured JSON-LD SEO markup, asset minification frameworks',
|
|
89
|
+
skill: 'seo-audit.md',
|
|
90
|
+
skillDesc: 'Lighthouse audits optimization guidelines and Core Web Vitals targets.'
|
|
91
|
+
},
|
|
92
|
+
'general-app': {
|
|
93
|
+
name: 'general-app',
|
|
94
|
+
description: 'Baseline generic fallback profile for standard backend systems (Python, Go, Node, Rust) and universal git workflows.',
|
|
95
|
+
stack: 'Universal backends baseline structure, default git flow parameters',
|
|
96
|
+
skill: 'example-skill.md',
|
|
97
|
+
skillDesc: 'Generic baseline instructions and coding standards.'
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
63
101
|
if (params.help || !COMMAND) {
|
|
64
102
|
showHelp();
|
|
65
103
|
process.exit(0);
|
|
@@ -69,6 +107,19 @@ if (COMMAND === 'init') {
|
|
|
69
107
|
handleInit(params);
|
|
70
108
|
} else if (COMMAND === 'verify') {
|
|
71
109
|
handleVerify(params);
|
|
110
|
+
} else if (COMMAND === 'templates' || COMMAND === 'list-templates') {
|
|
111
|
+
handleListTemplates();
|
|
112
|
+
} else if (COMMAND === 'show-template') {
|
|
113
|
+
const tName = ARGS[1];
|
|
114
|
+
if (!tName || tName.startsWith('-')) {
|
|
115
|
+
console.error('\x1b[31mError: Please specify a template name. Example: node bin/multimodel-dev-os.js show-template nextjs-saas\x1b[0m');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
handleShowTemplate(tName);
|
|
119
|
+
} else if (COMMAND === 'doctor') {
|
|
120
|
+
handleDoctor(params);
|
|
121
|
+
} else if (COMMAND === 'validate') {
|
|
122
|
+
handleValidate(params);
|
|
72
123
|
} else {
|
|
73
124
|
console.error(`\x1b[31mUnknown command: ${COMMAND}\x1b[0m`);
|
|
74
125
|
showHelp();
|
|
@@ -80,8 +131,13 @@ function showHelp() {
|
|
|
80
131
|
console.log('====================================');
|
|
81
132
|
console.log('Usage: node bin/multimodel-dev-os.js <command> [options]\n');
|
|
82
133
|
console.log('Commands:');
|
|
83
|
-
console.log(' init
|
|
84
|
-
console.log(' verify
|
|
134
|
+
console.log(' init Initialize a project with configs and adapters');
|
|
135
|
+
console.log(' verify Validate structural integrity of an existing project');
|
|
136
|
+
console.log(' templates List all built-in template profiles with details');
|
|
137
|
+
console.log(' list-templates Alias for templates command');
|
|
138
|
+
console.log(' show-template <t> Inspect detailed stack specifications of template <t>');
|
|
139
|
+
console.log(' doctor Advisory checkup of project compatibility loops and ignored folders');
|
|
140
|
+
console.log(' validate Strict validation checks to verify directory schema compliance\n');
|
|
85
141
|
console.log('Options:');
|
|
86
142
|
console.log(' -t, --target <path> Target folder destination (default: current working directory)');
|
|
87
143
|
console.log(' --template <name> Template profile: nextjs-saas, wordpress-site, ecommerce-store,');
|
|
@@ -92,6 +148,48 @@ function showHelp() {
|
|
|
92
148
|
console.log(' -f, --force Overwrite existing files without prompting\n');
|
|
93
149
|
}
|
|
94
150
|
|
|
151
|
+
function handleListTemplates() {
|
|
152
|
+
console.log(`\n🧠 \x1b[36mBuilt-in Template Profiles [v${version}]\x1b[0m`);
|
|
153
|
+
console.log('==================================================');
|
|
154
|
+
Object.keys(TEMPLATES).forEach(key => {
|
|
155
|
+
const t = TEMPLATES[key];
|
|
156
|
+
console.log(`\n\x1b[32m* ${t.name}\x1b[0m`);
|
|
157
|
+
console.log(` \x1b[33mStack:\x1b[0m ${t.stack}`);
|
|
158
|
+
console.log(` \x1b[37mDescription:\x1b[0m ${t.description}`);
|
|
159
|
+
});
|
|
160
|
+
console.log('\nUse \x1b[36mshow-template <template-name>\x1b[0m to view detailed layout specifications.\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function handleShowTemplate(name) {
|
|
164
|
+
const t = TEMPLATES[name];
|
|
165
|
+
if (!t) {
|
|
166
|
+
console.error(`\n\x1b[31mError: Template '${name}' does not exist. Available: nextjs-saas, wordpress-site, ecommerce-store, seo-landing-page, general-app\x1b[0m\n`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(`\n🔍 \x1b[36mTemplate Profile: ${t.name}\x1b[0m`);
|
|
171
|
+
console.log('==================================================');
|
|
172
|
+
console.log(`\x1b[33mStack Blueprint:\x1b[0m ${t.stack}`);
|
|
173
|
+
console.log(`\x1b[33mOverview:\x1b[0m ${t.description}`);
|
|
174
|
+
console.log(`\x1b[33mHighlighted Skill:\x1b[0m .ai/skills/${t.skill}`);
|
|
175
|
+
console.log(` └─> ${t.skillDesc}`);
|
|
176
|
+
console.log('\n\x1b[33mScaffolding Directory Layout:\x1b[0m');
|
|
177
|
+
console.log(' ├── AGENTS.md (Stack building conventions)');
|
|
178
|
+
console.log(' ├── MEMORY.md (Architectural constraints record)');
|
|
179
|
+
console.log(' ├── TASKS.md (Pre-populated first project tasks)');
|
|
180
|
+
console.log(' ├── RUNBOOK.md (Default operations guide)');
|
|
181
|
+
console.log(' └── .ai/');
|
|
182
|
+
console.log(' ├── config.yaml (Enabled adapter options)');
|
|
183
|
+
console.log(' ├── context/');
|
|
184
|
+
console.log(' │ ├── project-brief.md (Scaffolding baseline brief)');
|
|
185
|
+
console.log(' │ ├── architecture.md (Stack specific architecture map)');
|
|
186
|
+
console.log(' │ ├── model-map.md (AI routing specifications)');
|
|
187
|
+
console.log(' │ └── context-budget.md (Token allocation guidelines)');
|
|
188
|
+
console.log(` └── skills/`);
|
|
189
|
+
console.log(` └── ${t.skill} (Custom template skills code boiler)`);
|
|
190
|
+
console.log('\nUse \x1b[32minit --template ' + t.name + '\x1b[0m to bootstrap this profile.\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
95
193
|
function handleInit(options) {
|
|
96
194
|
console.log(`\n\x1b[34mInitializing multimodel-dev-os in: ${options.target}\x1b[0m`);
|
|
97
195
|
console.log(`Template profile: \x1b[32m${options.template}\x1b[0m`);
|
|
@@ -101,23 +199,18 @@ function handleInit(options) {
|
|
|
101
199
|
const operations = [];
|
|
102
200
|
const conflicts = [];
|
|
103
201
|
|
|
104
|
-
//
|
|
105
|
-
let
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
let runbookSrc = join(sourceRoot, 'RUNBOOK.md');
|
|
109
|
-
let configSrc = join(sourceRoot, '.ai', 'config.yaml');
|
|
110
|
-
|
|
111
|
-
// Load custom template directories if selected
|
|
112
|
-
if (options.template !== 'general-app') {
|
|
113
|
-
const templateDir = join(sourceRoot, 'examples', options.template);
|
|
114
|
-
if (existsSync(templateDir)) {
|
|
115
|
-
agentsSrc = join(templateDir, 'AGENTS.md');
|
|
116
|
-
memorySrc = join(templateDir, 'MEMORY.md');
|
|
117
|
-
configSrc = join(templateDir, '.ai', 'config.yaml');
|
|
118
|
-
}
|
|
202
|
+
// Source path mapping for core files
|
|
203
|
+
let templateDir = join(sourceRoot, 'examples', options.template);
|
|
204
|
+
if (!existsSync(templateDir)) {
|
|
205
|
+
templateDir = join(sourceRoot, 'examples', 'general-app');
|
|
119
206
|
}
|
|
120
207
|
|
|
208
|
+
let agentsSrc = join(templateDir, 'AGENTS.md');
|
|
209
|
+
let memorySrc = join(templateDir, 'MEMORY.md');
|
|
210
|
+
let tasksSrc = join(templateDir, 'TASKS.md');
|
|
211
|
+
let runbookSrc = join(sourceRoot, 'RUNBOOK.md'); // Global operational runbook fallback
|
|
212
|
+
let configSrc = join(templateDir, '.ai', 'config.yaml');
|
|
213
|
+
|
|
121
214
|
// Handle Caveman Mode overrides
|
|
122
215
|
if (options.caveman) {
|
|
123
216
|
agentsSrc = join(sourceRoot, '.ai', 'templates', 'AGENTS.caveman.md');
|
|
@@ -126,32 +219,55 @@ function handleInit(options) {
|
|
|
126
219
|
runbookSrc = join(sourceRoot, '.ai', 'templates', 'RUNBOOK.caveman.md');
|
|
127
220
|
}
|
|
128
221
|
|
|
129
|
-
// 1. Core Root Files
|
|
130
222
|
operations.push({ dest: 'AGENTS.md', src: agentsSrc });
|
|
131
223
|
operations.push({ dest: 'MEMORY.md', src: memorySrc });
|
|
132
224
|
operations.push({ dest: 'TASKS.md', src: tasksSrc });
|
|
133
225
|
operations.push({ dest: 'RUNBOOK.md', src: runbookSrc });
|
|
134
226
|
operations.push({ dest: '.ai/config.yaml', src: configSrc });
|
|
135
227
|
|
|
136
|
-
//
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
228
|
+
// Add all files from template-specific context and skills folders if they exist
|
|
229
|
+
const templateAiDir = join(templateDir, '.ai');
|
|
230
|
+
if (existsSync(templateAiDir) && !options.caveman) {
|
|
231
|
+
const subdirs = ['context', 'skills'];
|
|
232
|
+
subdirs.forEach(sub => {
|
|
233
|
+
const subPath = join(templateAiDir, sub);
|
|
234
|
+
if (existsSync(subPath)) {
|
|
235
|
+
readdirSync(subPath).forEach(file => {
|
|
236
|
+
operations.push({
|
|
237
|
+
dest: join('.ai', sub, file),
|
|
238
|
+
src: join(subPath, file)
|
|
239
|
+
});
|
|
145
240
|
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Fallback to copy default global folders if files aren't already included by template
|
|
246
|
+
const globalAiSubdirs = ['context', 'agents', 'skills', 'prompts', 'checks', 'templates', 'session-logs'];
|
|
247
|
+
globalAiSubdirs.forEach(sub => {
|
|
248
|
+
const globalPath = join(sourceRoot, '.ai', sub);
|
|
249
|
+
if (existsSync(globalPath)) {
|
|
250
|
+
readdirSync(globalPath).forEach(file => {
|
|
251
|
+
const destRel = join('.ai', sub, file);
|
|
252
|
+
// Only push if not already loaded from the template specific directory overrides
|
|
253
|
+
if (!operations.some(op => op.dest === destRel)) {
|
|
254
|
+
// If --caveman is active, skip regular context/skills to save token files
|
|
255
|
+
if (options.caveman && (sub === 'context' || sub === 'skills' || sub === 'prompts' || sub === 'checks')) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
operations.push({
|
|
259
|
+
dest: destRel,
|
|
260
|
+
src: join(globalPath, file)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
146
263
|
});
|
|
147
264
|
}
|
|
148
265
|
});
|
|
149
266
|
|
|
150
|
-
//
|
|
267
|
+
// Selected Adapters
|
|
151
268
|
options.adapters.forEach(adapter => {
|
|
152
269
|
const adapterDir = join(sourceRoot, 'adapters', adapter);
|
|
153
270
|
if (existsSync(adapterDir)) {
|
|
154
|
-
// Helper function to read recursive files in adapter
|
|
155
271
|
const copyRecursive = (currSrc, currRel) => {
|
|
156
272
|
if (statSync(currSrc).isDirectory()) {
|
|
157
273
|
readdirSync(currSrc).forEach(file => {
|
|
@@ -172,7 +288,7 @@ function handleInit(options) {
|
|
|
172
288
|
}
|
|
173
289
|
});
|
|
174
290
|
|
|
175
|
-
//
|
|
291
|
+
// Audit conflicts
|
|
176
292
|
operations.forEach(op => {
|
|
177
293
|
const targetFile = join(options.target, op.dest);
|
|
178
294
|
if (existsSync(targetFile)) {
|
|
@@ -207,6 +323,83 @@ function handleInit(options) {
|
|
|
207
323
|
}
|
|
208
324
|
});
|
|
209
325
|
|
|
326
|
+
// Ensure crucial directories exist (e.g. for --caveman or missing folders check compliance)
|
|
327
|
+
const dirsToEnsure = ['.ai/context', '.ai/skills', '.ai/session-logs'];
|
|
328
|
+
dirsToEnsure.forEach(d => {
|
|
329
|
+
const fullPath = join(options.target, d);
|
|
330
|
+
if (!options.dryRun && !existsSync(fullPath)) {
|
|
331
|
+
mkdirSync(fullPath, { recursive: true });
|
|
332
|
+
console.log(` \x1b[32mCREATE DIR:\x1b[0m ${d}`);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Copy root-level adapter rule files if selected
|
|
337
|
+
if (!options.dryRun) {
|
|
338
|
+
options.adapters.forEach(adapter => {
|
|
339
|
+
if (adapter === 'cursor') {
|
|
340
|
+
const srcFile = join(sourceRoot, 'adapters/cursor/.cursorrules');
|
|
341
|
+
const destFile = join(options.target, '.cursorrules');
|
|
342
|
+
if (existsSync(srcFile)) {
|
|
343
|
+
writeFileSync(destFile, readFileSync(srcFile));
|
|
344
|
+
console.log(` \x1b[32mCREATE ROOT ADAPTER FILE:\x1b[0m .cursorrules`);
|
|
345
|
+
}
|
|
346
|
+
} else if (adapter === 'claude') {
|
|
347
|
+
const srcFile = join(sourceRoot, 'adapters/claude/CLAUDE.md');
|
|
348
|
+
const destFile = join(options.target, 'CLAUDE.md');
|
|
349
|
+
if (existsSync(srcFile)) {
|
|
350
|
+
writeFileSync(destFile, readFileSync(srcFile));
|
|
351
|
+
console.log(` \x1b[32mCREATE ROOT ADAPTER FILE:\x1b[0m CLAUDE.md`);
|
|
352
|
+
}
|
|
353
|
+
} else if (adapter === 'vscode') {
|
|
354
|
+
const srcFile = join(sourceRoot, 'adapters/vscode/.vscode/settings.json');
|
|
355
|
+
const destDir = join(options.target, '.vscode');
|
|
356
|
+
const destFile = join(destDir, 'settings.json');
|
|
357
|
+
if (existsSync(srcFile)) {
|
|
358
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
359
|
+
writeFileSync(destFile, readFileSync(srcFile));
|
|
360
|
+
console.log(` \x1b[32mCREATE ROOT ADAPTER FILE:\x1b[0m .vscode/settings.json`);
|
|
361
|
+
}
|
|
362
|
+
} else if (adapter === 'gemini') {
|
|
363
|
+
const srcFile = join(sourceRoot, 'adapters/gemini/GEMINI.md');
|
|
364
|
+
const destFile = join(options.target, 'GEMINI.md');
|
|
365
|
+
if (existsSync(srcFile)) {
|
|
366
|
+
writeFileSync(destFile, readFileSync(srcFile));
|
|
367
|
+
console.log(` \x1b[32mCREATE ROOT ADAPTER FILE:\x1b[0m GEMINI.md`);
|
|
368
|
+
}
|
|
369
|
+
} else if (adapter === 'antigravity') {
|
|
370
|
+
const srcFile = join(sourceRoot, 'adapters/antigravity/.gemini/settings.json');
|
|
371
|
+
const destDir = join(options.target, '.gemini');
|
|
372
|
+
const destFile = join(destDir, 'settings.json');
|
|
373
|
+
if (existsSync(srcFile)) {
|
|
374
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
375
|
+
writeFileSync(destFile, readFileSync(srcFile));
|
|
376
|
+
console.log(` \x1b[32mCREATE ROOT ADAPTER FILE:\x1b[0m .gemini/settings.json`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Dynamically enable selected adapters in the target .ai/config.yaml
|
|
382
|
+
const targetConfigPath = join(options.target, '.ai/config.yaml');
|
|
383
|
+
if (existsSync(targetConfigPath) && options.adapters.length > 0) {
|
|
384
|
+
let configContent = readFileSync(targetConfigPath, 'utf8');
|
|
385
|
+
options.adapters.forEach(adapter => {
|
|
386
|
+
const regex = new RegExp(`${adapter}:\\s*false`, 'g');
|
|
387
|
+
configContent = configContent.replace(regex, `${adapter}: true`);
|
|
388
|
+
});
|
|
389
|
+
writeFileSync(targetConfigPath, configContent, 'utf8');
|
|
390
|
+
console.log(` \x1b[32mUPDATE CONFIG:\x1b[0m Enabled selected adapters [${options.adapters.join(', ')}] in .ai/config.yaml`);
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
// Dry run notes
|
|
394
|
+
options.adapters.forEach(adapter => {
|
|
395
|
+
if (adapter === 'cursor') console.log(` \x1b[36m[DRY-RUN] WOULD CREATE ROOT ADAPTER FILE:\x1b[0m .cursorrules`);
|
|
396
|
+
else if (adapter === 'claude') console.log(` \x1b[36m[DRY-RUN] WOULD CREATE ROOT ADAPTER FILE:\x1b[0m CLAUDE.md`);
|
|
397
|
+
else if (adapter === 'vscode') console.log(` \x1b[36m[DRY-RUN] WOULD CREATE ROOT ADAPTER FILE:\x1b[0m .vscode/settings.json`);
|
|
398
|
+
else if (adapter === 'gemini') console.log(` \x1b[36m[DRY-RUN] WOULD CREATE ROOT ADAPTER FILE:\x1b[0m GEMINI.md`);
|
|
399
|
+
else if (adapter === 'antigravity') console.log(` \x1b[36m[DRY-RUN] WOULD CREATE ROOT ADAPTER FILE:\x1b[0m .gemini/settings.json`);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
210
403
|
console.log(`\n\x1b[32m✔ Project initialized successfully! [Total Operations: ${operations.length}]\x1b[0m\n`);
|
|
211
404
|
}
|
|
212
405
|
|
|
@@ -227,11 +420,9 @@ function handleVerify(options) {
|
|
|
227
420
|
}
|
|
228
421
|
};
|
|
229
422
|
|
|
230
|
-
// 1. Core Files
|
|
231
423
|
const rootFiles = ['AGENTS.md', 'MEMORY.md', 'TASKS.md', 'RUNBOOK.md', '.ai/config.yaml'];
|
|
232
424
|
rootFiles.forEach(assertFile);
|
|
233
425
|
|
|
234
|
-
// 2. .ai/context
|
|
235
426
|
const contextFiles = [
|
|
236
427
|
'.ai/context/project-brief.md',
|
|
237
428
|
'.ai/context/architecture.md',
|
|
@@ -243,7 +434,6 @@ function handleVerify(options) {
|
|
|
243
434
|
];
|
|
244
435
|
contextFiles.forEach(assertFile);
|
|
245
436
|
|
|
246
|
-
// 3. .ai/agents
|
|
247
437
|
const agentFiles = [
|
|
248
438
|
'.ai/agents/multimodel-orchestrator.md',
|
|
249
439
|
'.ai/agents/planner.md',
|
|
@@ -265,3 +455,198 @@ function handleVerify(options) {
|
|
|
265
455
|
process.exit(0);
|
|
266
456
|
}
|
|
267
457
|
}
|
|
458
|
+
|
|
459
|
+
function handleDoctor(options) {
|
|
460
|
+
console.log(`\n🩺 \x1b[36mRunning advisory doctor checkup in: ${options.target}\x1b[0m\n`);
|
|
461
|
+
|
|
462
|
+
let warnings = 0;
|
|
463
|
+
|
|
464
|
+
const warn = (msg) => {
|
|
465
|
+
console.warn(` \x1b[33m[WARNING]\x1b[0m ${msg}`);
|
|
466
|
+
warnings++;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// 1. .gitignore checks
|
|
470
|
+
const gitignorePath = join(options.target, '.gitignore');
|
|
471
|
+
if (existsSync(gitignorePath)) {
|
|
472
|
+
const content = readFileSync(gitignorePath, 'utf8');
|
|
473
|
+
if (!content.includes('node_modules')) {
|
|
474
|
+
warn('.gitignore is missing node_modules! This will cause AI tools to choke by scanning dependencies.');
|
|
475
|
+
}
|
|
476
|
+
if (!content.includes('.env')) {
|
|
477
|
+
warn('.gitignore is missing .env config boundaries! Secret tokens might get exposed to models.');
|
|
478
|
+
}
|
|
479
|
+
} else {
|
|
480
|
+
warn('Missing .gitignore file in target workspace! AI tools might read large build artifacts.');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// 2. Build/test/lint presence inside AGENTS.md
|
|
484
|
+
const agentsPath = join(options.target, 'AGENTS.md');
|
|
485
|
+
if (existsSync(agentsPath)) {
|
|
486
|
+
const content = readFileSync(agentsPath, 'utf8');
|
|
487
|
+
if (!content.includes('build:') && !content.includes('build')) {
|
|
488
|
+
warn('AGENTS.md is missing build command specifications.');
|
|
489
|
+
}
|
|
490
|
+
if (!content.includes('test:') && !content.includes('test')) {
|
|
491
|
+
warn('AGENTS.md is missing test command specifications.');
|
|
492
|
+
}
|
|
493
|
+
if (!content.includes('lint:') && !content.includes('lint')) {
|
|
494
|
+
warn('AGENTS.md is missing lint command specifications.');
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
warn('AGENTS.md is missing from project root.');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// 3. Null placeholders check in MEMORY.md
|
|
501
|
+
const memoryPath = join(options.target, 'MEMORY.md');
|
|
502
|
+
if (existsSync(memoryPath)) {
|
|
503
|
+
const content = readFileSync(memoryPath, 'utf8');
|
|
504
|
+
const placeholdersCount = (content.match(/null/g) || []).length;
|
|
505
|
+
if (placeholdersCount > 3) {
|
|
506
|
+
warn(`MEMORY.md contains ${placeholdersCount} empty 'null' placeholders. Update project constraints.`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// 4. Tasks checklist status
|
|
511
|
+
const tasksPath = join(options.target, 'TASKS.md');
|
|
512
|
+
if (existsSync(tasksPath)) {
|
|
513
|
+
const content = readFileSync(tasksPath, 'utf8');
|
|
514
|
+
if (!content.includes('- [ ]') && !content.includes('- [/]')) {
|
|
515
|
+
warn('TASKS.md has no active task section (no tasks marked as - [ ] or - [/]).');
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
warn('TASKS.md is missing from project root.');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 5. Active adapters files audit
|
|
522
|
+
const configPath = join(options.target, '.ai', 'config.yaml');
|
|
523
|
+
if (existsSync(configPath)) {
|
|
524
|
+
const content = readFileSync(configPath, 'utf8');
|
|
525
|
+
const checkAdapter = (adapterName, filename) => {
|
|
526
|
+
const regex = new RegExp(`${adapterName}:\\s*true`);
|
|
527
|
+
if (regex.test(content)) {
|
|
528
|
+
const filePath = join(options.target, filename);
|
|
529
|
+
if (!existsSync(filePath)) {
|
|
530
|
+
warn(`Adapter '${adapterName}' is enabled in .ai/config.yaml but matching adapter file '${filename}' is missing from root.`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
checkAdapter('cursor', '.cursorrules');
|
|
535
|
+
checkAdapter('claude', 'CLAUDE.md');
|
|
536
|
+
checkAdapter('gemini', 'GEMINI.md');
|
|
537
|
+
checkAdapter('vscode', '.vscode/settings.json');
|
|
538
|
+
checkAdapter('antigravity', '.gemini/settings.json');
|
|
539
|
+
} else {
|
|
540
|
+
warn('.ai/config.yaml is missing from project. Active adapters could not be audited.');
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 6. Token sinks audit
|
|
544
|
+
const sinkFolders = ['node_modules', 'dist', 'build', '.next', '.git'];
|
|
545
|
+
sinkFolders.forEach(folder => {
|
|
546
|
+
const fullPath = join(options.target, folder);
|
|
547
|
+
if (existsSync(fullPath)) {
|
|
548
|
+
const gitignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf8') : '';
|
|
549
|
+
if (!gitignore.includes(folder)) {
|
|
550
|
+
warn(`Large token-sink directory '${folder}/' is present in workspace but not ignored in .gitignore. AI tools may read it.`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
console.log('\n==================================================');
|
|
556
|
+
if (warnings > 0) {
|
|
557
|
+
console.log(`\x1b[33mDoctor checkup complete. Found ${warnings} advisory warnings.\x1b[0m\n`);
|
|
558
|
+
} else {
|
|
559
|
+
console.log('\x1b[32m✔ Doctor checkup complete. Your project context layout is pristine!\x1b[0m\n');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function handleValidate(options) {
|
|
564
|
+
console.log(`\n🛡 \x1b[34mRunning strict schema validation in: ${options.target}\x1b[0m\n`);
|
|
565
|
+
|
|
566
|
+
let errors = 0;
|
|
567
|
+
|
|
568
|
+
const assertPath = (relPath, type) => {
|
|
569
|
+
const fullPath = join(options.target, relPath);
|
|
570
|
+
if (existsSync(fullPath)) {
|
|
571
|
+
const stat = statSync(fullPath);
|
|
572
|
+
const isOk = (type === 'file') ? stat.isFile() : stat.isDirectory();
|
|
573
|
+
if (isOk) {
|
|
574
|
+
console.log(` \x1b[32m✓\x1b[0m ${relPath} (${type})`);
|
|
575
|
+
} else {
|
|
576
|
+
console.error(` \x1b[31m✗ ${relPath} (expected to be a ${type})\x1b[0m`);
|
|
577
|
+
errors++;
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
console.error(` \x1b[31m✗ ${relPath} (missing)\x1b[0m`);
|
|
581
|
+
errors++;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
// 1. Assert Core files
|
|
586
|
+
const core = ['AGENTS.md', 'MEMORY.md', 'TASKS.md', 'RUNBOOK.md', '.ai/config.yaml'];
|
|
587
|
+
core.forEach(f => assertPath(f, 'file'));
|
|
588
|
+
|
|
589
|
+
// 2. Assert Core folders (excluding agents first)
|
|
590
|
+
const dirs = ['.ai/context', '.ai/skills', '.ai/session-logs'];
|
|
591
|
+
dirs.forEach(d => assertPath(d, 'dir'));
|
|
592
|
+
|
|
593
|
+
// 3. Assert .ai/agents exists OR global agent use is explained in AGENTS.md
|
|
594
|
+
const agentsPath = join(options.target, '.ai/agents');
|
|
595
|
+
const agentsExist = existsSync(agentsPath) && statSync(agentsPath).isDirectory();
|
|
596
|
+
if (agentsExist) {
|
|
597
|
+
console.log(` \x1b[32m✓\x1b[0m .ai/agents (dir)`);
|
|
598
|
+
} else {
|
|
599
|
+
const agentsMdPath = join(options.target, 'AGENTS.md');
|
|
600
|
+
let explained = false;
|
|
601
|
+
if (existsSync(agentsMdPath)) {
|
|
602
|
+
const agentsMdContent = readFileSync(agentsMdPath, 'utf8');
|
|
603
|
+
if (
|
|
604
|
+
agentsMdContent.includes('multimodel') ||
|
|
605
|
+
agentsMdContent.includes('orchestrator') ||
|
|
606
|
+
agentsMdContent.includes('global') ||
|
|
607
|
+
agentsMdContent.includes('role') ||
|
|
608
|
+
agentsMdContent.includes('Agent Roles')
|
|
609
|
+
) {
|
|
610
|
+
explained = true;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (explained) {
|
|
614
|
+
console.log(` \x1b[32m✓\x1b[0m .ai/agents (missing, but global agent/orchestrator usage explained in AGENTS.md)`);
|
|
615
|
+
} else {
|
|
616
|
+
console.error(` \x1b[31m✗ .ai/agents (missing and global agent use is not explained in AGENTS.md)\x1b[0m`);
|
|
617
|
+
errors++;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// 4. Assert Active adapters files (adapter references are not broken)
|
|
622
|
+
const configPath = join(options.target, '.ai', 'config.yaml');
|
|
623
|
+
if (existsSync(configPath)) {
|
|
624
|
+
const content = readFileSync(configPath, 'utf8');
|
|
625
|
+
const assertAdapter = (adapterName, filename) => {
|
|
626
|
+
const regex = new RegExp(`${adapterName}:\\s*true`);
|
|
627
|
+
if (regex.test(content)) {
|
|
628
|
+
const fullPath = join(options.target, filename);
|
|
629
|
+
if (existsSync(fullPath)) {
|
|
630
|
+
console.log(` \x1b[32m✓\x1b[0m ${filename} (enabled adapter rules file verified)`);
|
|
631
|
+
} else {
|
|
632
|
+
console.error(` \x1b[31m✗ ${filename} (adapter '${adapterName}' is enabled in .ai/config.yaml, but rule file is missing!)\x1b[0m`);
|
|
633
|
+
errors++;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
assertAdapter('cursor', '.cursorrules');
|
|
638
|
+
assertAdapter('claude', 'CLAUDE.md');
|
|
639
|
+
assertAdapter('gemini', 'GEMINI.md');
|
|
640
|
+
assertAdapter('vscode', '.vscode/settings.json');
|
|
641
|
+
assertAdapter('antigravity', '.gemini/settings.json');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
console.log('\n==================================================');
|
|
645
|
+
if (errors > 0) {
|
|
646
|
+
console.error(` \x1b[31mValidation FAILED. Found ${errors} strict structural compliance errors.\x1b[0m\n`);
|
|
647
|
+
process.exit(1);
|
|
648
|
+
} else {
|
|
649
|
+
console.log(' \x1b[32m✔ Validation PASSED. Your project context structure is strictly compliant!\x1b[0m\n');
|
|
650
|
+
process.exit(0);
|
|
651
|
+
}
|
|
652
|
+
}
|
package/docs/caveman-mode.md
CHANGED
|
@@ -21,12 +21,16 @@ AI agents consume tokens for every file they read. In large projects, context wi
|
|
|
21
21
|
| `RUNBOOK.md` | ~400 tokens | ~80 tokens | 80% |
|
|
22
22
|
| **Total** | **~1,600** | **~340** | **~79%** |
|
|
23
23
|
|
|
24
|
-
## How to Use
|
|
25
|
-
|
|
26
24
|
### Fresh Install
|
|
27
25
|
|
|
26
|
+
**Via npx (Recommended):**
|
|
27
|
+
```bash
|
|
28
|
+
npx multimodel-dev-os@latest init --caveman
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Via shell script installer:**
|
|
28
32
|
```bash
|
|
29
|
-
curl -fsSL
|
|
33
|
+
curl -fsSL https://raw.githubusercontent.com/rizvee/multimodel-dev-os/main/scripts/install.sh | bash -s -- --caveman
|
|
30
34
|
```
|
|
31
35
|
|
|
32
36
|
### Convert Existing Project
|
package/docs/cli-roadmap.md
CHANGED
|
@@ -1,44 +1,56 @@
|
|
|
1
1
|
# CLI Roadmap
|
|
2
2
|
|
|
3
|
-
> The
|
|
3
|
+
> The zero-dependency CLI utility is fully integrated with `npm` and `npx` in v0.3.0!
|
|
4
4
|
|
|
5
5
|
## Current CLI Usage
|
|
6
6
|
|
|
7
|
+
Recommended way to execute the CLI globally via npx:
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
|
-
# Initialize
|
|
9
|
-
|
|
10
|
+
# Initialize standard configuration in current directory
|
|
11
|
+
npx multimodel-dev-os@latest init
|
|
10
12
|
|
|
11
13
|
# Initialize with specific template and adapters
|
|
12
|
-
|
|
14
|
+
npx multimodel-dev-os@latest init --template nextjs-saas --adapter cursor --adapter claude
|
|
13
15
|
|
|
14
|
-
# Run dry-run preview
|
|
15
|
-
|
|
16
|
+
# Run dry-run preview before executing file writes
|
|
17
|
+
npx multimodel-dev-os@latest init --dry-run
|
|
16
18
|
|
|
17
19
|
# Force overwrite existing files
|
|
18
|
-
|
|
20
|
+
npx multimodel-dev-os@latest init --force
|
|
19
21
|
|
|
20
22
|
# Check structural health of target directory
|
|
21
|
-
|
|
23
|
+
npx multimodel-dev-os@latest verify
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
Alternatively, you can run the CLI locally within a cloned workspace:
|
|
27
|
+
```bash
|
|
28
|
+
node bin/multimodel-dev-os.js init
|
|
29
|
+
node bin/multimodel-dev-os.js verify
|
|
30
|
+
```
|
|
25
31
|
|
|
26
32
|
| Command | Purpose | Target Version | Status |
|
|
27
33
|
|---------|---------|----------------|--------|
|
|
28
34
|
| `init` | Scaffold multimodel-dev-os into a project | v0.2.0 | ✅ Completed |
|
|
29
35
|
| `verify` | Check that all required files exist and are valid | v0.2.0 | ✅ Completed |
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
36
|
+
| `templates` | List all built-in template profiles with details | v0.5.0 | ✅ Completed |
|
|
37
|
+
| `show-template` | Inspect stack specifications of a template | v0.5.0 | ✅ Completed |
|
|
38
|
+
| `doctor` | Advisory checkup of workspace compatibility | v0.5.0 | ✅ Completed |
|
|
39
|
+
| `validate` | Strict directory schema compliance checks | v0.5.0 | ✅ Completed |
|
|
40
|
+
| `sync` | Regenerate adapter files from root AGENTS.md | v0.6.0 | 📋 Planned |
|
|
41
|
+
| `add-adapter` | Add a new adapter to the project | v0.6.0 | 📋 Planned |
|
|
42
|
+
|
|
43
|
+
## Requirements Completed in v0.5.0
|
|
44
|
+
|
|
45
|
+
- [x] Implemented strict `validate` CLI command for structural directory validation.
|
|
46
|
+
- [x] Implemented advisory `doctor` command for project compatibility warnings.
|
|
47
|
+
- [x] Implemented `templates` and `show-template` commands for built-in profiles inspection.
|
|
48
|
+
- [x] Upgraded all 5 built-in template profiles with practical real-world contents.
|
|
49
|
+
- [x] Implemented dynamic context budgetary constraints and skills validation.
|
|
50
|
+
- [x] Preserved zero-dependency pure Node CLI implementations.
|
|
32
51
|
|
|
33
|
-
##
|
|
52
|
+
## Future Releases (v0.6.0+)
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
- [x] Template profile injection (Next.js SaaS, WordPress, etc.)
|
|
38
|
-
- [x] Conflict protection and `--force` overrides
|
|
39
|
-
- [x] Dry-run preview mode
|
|
40
|
-
- [x] Tested on Node 18+ and Windows/macOS/Linux
|
|
54
|
+
* **Adapter Autoregeneration (`sync`):** Parse custom override boundaries inside adapters and automatically synchronize them with updates in the root markdown source of truth.
|
|
55
|
+
* **Interactive Mode:** Provide step-by-step CLI options if run without arguments.
|
|
41
56
|
|
|
42
|
-
## Future Releases (v0.3.0+)
|
|
43
|
-
- Publish package to npm as `multimodel-dev-os` to support `npx` execution.
|
|
44
|
-
- Implement the `sync` subcommand to parse custom override markers inside adapters and align them with changes in root instructions.
|