nlos 1.0.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/bin/nlos.js ADDED
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * NL-OS CLI - Natural Language Operating System
5
+ *
6
+ * A model-agnostic kernel that turns any LLM into a cognitive operating system.
7
+ *
8
+ * Usage:
9
+ * nlos boot [options] Boot NL-OS with a local LLM
10
+ * nlos payload [options] Generate portable kernel payloads
11
+ * nlos verify Verify kernel files exist
12
+ * nlos tokens Show token estimates
13
+ * nlos help Show this help message
14
+ */
15
+
16
+ const { execSync, spawn } = require('child_process');
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+
20
+ // Resolve package root (handles both npm install and local dev)
21
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
22
+
23
+ // Kernel file definitions with token estimates
24
+ const KERNEL_FILES = {
25
+ mandatory: [
26
+ { file: 'memory.md', tokens: 4600 },
27
+ { file: 'AGENTS.md', tokens: 1200 },
28
+ { file: 'axioms.yaml', tokens: 4800 },
29
+ ],
30
+ lazy: [
31
+ { file: 'personalities.md', tokens: 3600 },
32
+ { file: '.cursor/commands/COMMAND-MAP.md', tokens: 1350 },
33
+ ],
34
+ extended: [
35
+ { file: 'projects/README.md', tokens: 1000 },
36
+ { file: 'KERNEL.yaml', tokens: 500 },
37
+ ],
38
+ };
39
+
40
+ // ANSI colors
41
+ const colors = {
42
+ reset: '\x1b[0m',
43
+ red: '\x1b[31m',
44
+ green: '\x1b[32m',
45
+ yellow: '\x1b[33m',
46
+ blue: '\x1b[34m',
47
+ cyan: '\x1b[36m',
48
+ };
49
+
50
+ function log(color, message) {
51
+ console.log(`${colors[color]}${message}${colors.reset}`);
52
+ }
53
+
54
+ function fileExists(relativePath) {
55
+ return fs.existsSync(path.join(PACKAGE_ROOT, relativePath));
56
+ }
57
+
58
+ function readFile(relativePath) {
59
+ const fullPath = path.join(PACKAGE_ROOT, relativePath);
60
+ if (fs.existsSync(fullPath)) {
61
+ return fs.readFileSync(fullPath, 'utf-8');
62
+ }
63
+ return `# File not found: ${relativePath}\n`;
64
+ }
65
+
66
+ function verifyFiles() {
67
+ log('blue', 'Verifying kernel files...\n');
68
+
69
+ let allExist = true;
70
+
71
+ for (const [tier, files] of Object.entries(KERNEL_FILES)) {
72
+ console.log(` ${tier.toUpperCase()} tier:`);
73
+ for (const { file, tokens } of files) {
74
+ const exists = fileExists(file);
75
+ const status = exists ? '[x]' : '[ ]';
76
+ const color = exists ? 'green' : 'red';
77
+
78
+ if (exists) {
79
+ const stats = fs.statSync(path.join(PACKAGE_ROOT, file));
80
+ log(color, ` ${status} ${file} (${stats.size.toLocaleString()} bytes)`);
81
+ } else {
82
+ log(color, ` ${status} ${file} (MISSING)`);
83
+ if (tier === 'mandatory') allExist = false;
84
+ }
85
+ }
86
+ console.log();
87
+ }
88
+
89
+ return allExist;
90
+ }
91
+
92
+ function showTokens() {
93
+ log('blue', 'Token estimates by tier:\n');
94
+
95
+ for (const tier of ['mandatory', 'lazy', 'extended']) {
96
+ let files = [...KERNEL_FILES.mandatory];
97
+ if (tier === 'lazy' || tier === 'extended') {
98
+ files = [...files, ...KERNEL_FILES.lazy];
99
+ }
100
+ if (tier === 'extended') {
101
+ files = [...files, ...KERNEL_FILES.extended];
102
+ }
103
+
104
+ const total = files.reduce((sum, f) => sum + f.tokens, 0);
105
+ log('cyan', `${tier.toUpperCase()} tier: ~${total.toLocaleString()} tokens`);
106
+
107
+ for (const { file, tokens } of files) {
108
+ console.log(` - ${file}: ~${tokens.toLocaleString()}`);
109
+ }
110
+ console.log();
111
+ }
112
+ }
113
+
114
+ function generatePayload(tier = 'mandatory', format = 'markdown') {
115
+ let files = [...KERNEL_FILES.mandatory];
116
+ if (tier === 'lazy' || tier === 'full') {
117
+ files = [...files, ...KERNEL_FILES.lazy];
118
+ }
119
+ if (tier === 'full') {
120
+ files = [...files, ...KERNEL_FILES.extended];
121
+ }
122
+
123
+ const sections = files.map(({ file, tokens }) => ({
124
+ file,
125
+ content: readFile(file),
126
+ tokens,
127
+ }));
128
+
129
+ const totalTokens = sections.reduce((sum, s) => sum + s.tokens, 0);
130
+ const timestamp = new Date().toISOString().split('T')[0];
131
+
132
+ if (format === 'json') {
133
+ return JSON.stringify({
134
+ metadata: {
135
+ generated: new Date().toISOString(),
136
+ generator: 'nlos',
137
+ tier,
138
+ total_estimated_tokens: totalTokens,
139
+ },
140
+ files: sections.map(s => ({ filename: s.file, content: s.content })),
141
+ }, null, 2);
142
+ }
143
+
144
+ // Markdown format
145
+ let output = `# NL-OS Kernel Payload
146
+
147
+ **Generated**: ${timestamp}
148
+ **Tier**: ${tier}
149
+ **Estimated tokens**: ~${totalTokens.toLocaleString()}
150
+
151
+ ---
152
+
153
+ ## How to Use
154
+
155
+ Paste this entire file as system prompt or context to any LLM.
156
+ After loading, the model should acknowledge: "Kernel loaded. Ready for capturebox operations."
157
+
158
+ ---
159
+
160
+ `;
161
+
162
+ for (const s of sections) {
163
+ output += `\n## ${s.file}\n\n${s.content}\n\n---\n`;
164
+ }
165
+
166
+ return output;
167
+ }
168
+
169
+ function boot(options = {}) {
170
+ const {
171
+ model = 'qwen2.5:3b',
172
+ full = false,
173
+ dryRun = false,
174
+ profile = null,
175
+ runtime = 'ollama',
176
+ } = options;
177
+
178
+ // Resolve model based on profile
179
+ let selectedModel = model;
180
+ if (profile) {
181
+ const profiles = {
182
+ speed: 'qwen2.5:3b',
183
+ balanced: 'mistral:7b',
184
+ quality: 'llama3.1:8b',
185
+ memory_constrained: 'qwen2.5:3b',
186
+ };
187
+ selectedModel = profiles[profile] || model;
188
+ }
189
+
190
+ log('blue', `Booting NL-OS via ${runtime}...`);
191
+ log('cyan', `Model: ${selectedModel}`);
192
+ log('cyan', `Tier: ${full ? 'FULL' : 'MANDATORY'}`);
193
+ console.log();
194
+
195
+ // Check for Python script first (more features)
196
+ const scriptPath = path.join(PACKAGE_ROOT, 'scripts', `kernel-boot-${runtime}.sh`);
197
+
198
+ if (fs.existsSync(scriptPath)) {
199
+ const args = [];
200
+ if (selectedModel !== 'qwen2.5:3b') args.push('--model', selectedModel);
201
+ if (full) args.push('--full');
202
+ if (dryRun) args.push('--dry-run');
203
+
204
+ if (dryRun) {
205
+ // For dry-run, capture and display output
206
+ try {
207
+ const output = execSync(`${scriptPath} ${args.join(' ')}`, {
208
+ encoding: 'utf-8',
209
+ cwd: PACKAGE_ROOT,
210
+ });
211
+ console.log(output);
212
+ } catch (error) {
213
+ log('red', `Error: ${error.message}`);
214
+ process.exit(1);
215
+ }
216
+ } else {
217
+ // For interactive, spawn with stdio inheritance
218
+ const child = spawn(scriptPath, args, {
219
+ cwd: PACKAGE_ROOT,
220
+ stdio: 'inherit',
221
+ });
222
+
223
+ child.on('error', (error) => {
224
+ log('red', `Error: ${error.message}`);
225
+ process.exit(1);
226
+ });
227
+
228
+ child.on('exit', (code) => {
229
+ process.exit(code || 0);
230
+ });
231
+ }
232
+ } else {
233
+ // Fallback: generate payload and show instructions
234
+ log('yellow', `Boot script not found for ${runtime}.`);
235
+ log('yellow', 'Generating payload instead...\n');
236
+
237
+ const payload = generatePayload(full ? 'full' : 'mandatory', 'markdown');
238
+ const outputPath = path.join(PACKAGE_ROOT, 'portable', 'kernel-payload-temp.md');
239
+
240
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
241
+ fs.writeFileSync(outputPath, payload);
242
+
243
+ log('green', `Payload saved to: ${outputPath}`);
244
+ console.log('\nTo boot manually:');
245
+ console.log(` cat ${outputPath} | pbcopy # Copy to clipboard`);
246
+ console.log(' # Paste into your LLM chat');
247
+ }
248
+ }
249
+
250
+ function payload(options = {}) {
251
+ const {
252
+ tier = 'mandatory',
253
+ format = 'markdown',
254
+ output = null,
255
+ all = false,
256
+ } = options;
257
+
258
+ const portableDir = path.join(PACKAGE_ROOT, 'portable');
259
+ fs.mkdirSync(portableDir, { recursive: true });
260
+
261
+ if (all) {
262
+ const variants = [
263
+ { tier: 'mandatory', format: 'markdown', name: 'kernel-payload.md' },
264
+ { tier: 'mandatory', format: 'json', name: 'kernel-payload.json' },
265
+ { tier: 'full', format: 'markdown', name: 'kernel-payload-full.md' },
266
+ { tier: 'full', format: 'json', name: 'kernel-payload-full.json' },
267
+ ];
268
+
269
+ for (const v of variants) {
270
+ const content = generatePayload(v.tier, v.format);
271
+ const outputPath = path.join(portableDir, v.name);
272
+ fs.writeFileSync(outputPath, content);
273
+ log('green', `Generated: ${outputPath}`);
274
+ }
275
+
276
+ console.log(`\nGenerated ${variants.length} payload files in ${portableDir}/`);
277
+ } else {
278
+ const content = generatePayload(tier, format);
279
+
280
+ if (output) {
281
+ fs.writeFileSync(output, content);
282
+ log('green', `Payload saved to: ${output}`);
283
+ } else {
284
+ const ext = format === 'json' ? 'json' : 'md';
285
+ const suffix = tier === 'mandatory' ? '' : `-${tier}`;
286
+ const outputPath = path.join(portableDir, `kernel-payload${suffix}.${ext}`);
287
+ fs.writeFileSync(outputPath, content);
288
+ log('green', `Payload saved to: ${outputPath}`);
289
+ }
290
+ }
291
+ }
292
+
293
+ function showHelp() {
294
+ console.log(`
295
+ ${colors.cyan}NL-OS${colors.reset} - Natural Language Operating System
296
+
297
+ ${colors.yellow}Usage:${colors.reset}
298
+ nlos <command> [options]
299
+
300
+ ${colors.yellow}Commands:${colors.reset}
301
+ boot Boot NL-OS with a local LLM
302
+ payload Generate portable kernel payloads
303
+ verify Verify kernel files exist
304
+ tokens Show token estimates
305
+ help Show this help message
306
+
307
+ ${colors.yellow}Boot Options:${colors.reset}
308
+ --model <name> Model to use (default: qwen2.5:3b)
309
+ --profile <name> Use profile: speed, balanced, quality, memory_constrained
310
+ --full Load full kernel (includes personalities)
311
+ --dry-run Preview system prompt without launching
312
+ --runtime <name> Runtime: ollama, llama-cpp, lm-studio (default: ollama)
313
+
314
+ ${colors.yellow}Payload Options:${colors.reset}
315
+ --tier <name> Tier: mandatory, lazy, full (default: mandatory)
316
+ --format <name> Format: markdown, json (default: markdown)
317
+ --output <path> Output file path
318
+ --all Generate all variants
319
+
320
+ ${colors.yellow}Examples:${colors.reset}
321
+ nlos boot # Boot with default model
322
+ nlos boot --model llama3.1:8b # Boot with specific model
323
+ nlos boot --profile quality --full # Quality mode with full kernel
324
+ nlos boot --dry-run # Preview system prompt
325
+ nlos payload # Generate default payload
326
+ nlos payload --all # Generate all payloads
327
+ nlos verify # Check kernel files
328
+ nlos tokens # Show token counts
329
+
330
+ ${colors.yellow}More Info:${colors.reset}
331
+ Repository: https://github.com/yourusername/capturebox
332
+ Quick Start: ${PACKAGE_ROOT}/QUICKSTART.md
333
+ `);
334
+ }
335
+
336
+ function parseArgs(args) {
337
+ const options = {};
338
+ let i = 0;
339
+
340
+ while (i < args.length) {
341
+ const arg = args[i];
342
+
343
+ if (arg === '--model' && args[i + 1]) {
344
+ options.model = args[++i];
345
+ } else if (arg === '--profile' && args[i + 1]) {
346
+ options.profile = args[++i];
347
+ } else if (arg === '--tier' && args[i + 1]) {
348
+ options.tier = args[++i];
349
+ } else if (arg === '--format' && args[i + 1]) {
350
+ options.format = args[++i];
351
+ } else if (arg === '--output' && args[i + 1]) {
352
+ options.output = args[++i];
353
+ } else if (arg === '--runtime' && args[i + 1]) {
354
+ options.runtime = args[++i];
355
+ } else if (arg === '--full') {
356
+ options.full = true;
357
+ } else if (arg === '--dry-run') {
358
+ options.dryRun = true;
359
+ } else if (arg === '--all') {
360
+ options.all = true;
361
+ }
362
+
363
+ i++;
364
+ }
365
+
366
+ return options;
367
+ }
368
+
369
+ // Main
370
+ const args = process.argv.slice(2);
371
+ const command = args[0];
372
+ const options = parseArgs(args.slice(1));
373
+
374
+ switch (command) {
375
+ case 'boot':
376
+ boot(options);
377
+ break;
378
+
379
+ case 'payload':
380
+ payload(options);
381
+ break;
382
+
383
+ case 'verify':
384
+ const allExist = verifyFiles();
385
+ process.exit(allExist ? 0 : 1);
386
+ break;
387
+
388
+ case 'tokens':
389
+ showTokens();
390
+ break;
391
+
392
+ case 'help':
393
+ case '--help':
394
+ case '-h':
395
+ case undefined:
396
+ showHelp();
397
+ break;
398
+
399
+ default:
400
+ log('red', `Unknown command: ${command}`);
401
+ console.log('Run "nlos help" for usage information.');
402
+ process.exit(1);
403
+ }