@sumant.pathak/devjar 1.0.1 ā 1.0.3
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/CLAUDE.md +38 -0
- package/bin/devjar.js +7 -0
- package/package.json +1 -1
- package/src/init.js +2 -1
- package/src/setup.js +340 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @sumant.pathak/devjar ā Claude Code Orientation
|
|
2
|
+
|
|
3
|
+
## Stack
|
|
4
|
+
Node.js
|
|
5
|
+
|
|
6
|
+
## Scripts
|
|
7
|
+
start: node bin/devjar.js
|
|
8
|
+
|
|
9
|
+
## Project Map
|
|
10
|
+
š bin
|
|
11
|
+
š devjar.js
|
|
12
|
+
š LICENSE
|
|
13
|
+
š package.json
|
|
14
|
+
š README.md
|
|
15
|
+
š src
|
|
16
|
+
š config.js
|
|
17
|
+
š init.js
|
|
18
|
+
š prompt.js
|
|
19
|
+
š providers
|
|
20
|
+
š anthropic.js
|
|
21
|
+
š gemini.js
|
|
22
|
+
š index.js
|
|
23
|
+
š ollama.js
|
|
24
|
+
š openai.js
|
|
25
|
+
š stats.js
|
|
26
|
+
š update.js
|
|
27
|
+
|
|
28
|
+
## Key Files Analyzed
|
|
29
|
+
- src/init.js
|
|
30
|
+
- src/update.js
|
|
31
|
+
|
|
32
|
+
## Unbreakable Rules
|
|
33
|
+
1. Match existing naming conventions before adding new code
|
|
34
|
+
2. Never write files outside the project root
|
|
35
|
+
3. Read the key files listed in Project Map before editing
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
*Generated by devjar init ā do not edit manually, run `devjar init` to regenerate*
|
package/bin/devjar.js
CHANGED
|
@@ -6,6 +6,7 @@ import { prompt } from '../src/prompt.js';
|
|
|
6
6
|
import { update } from '../src/update.js';
|
|
7
7
|
import { stats } from '../src/stats.js';
|
|
8
8
|
import { configCommand } from '../src/config.js';
|
|
9
|
+
import { setup } from '../src/setup.js';
|
|
9
10
|
|
|
10
11
|
const program = new Command();
|
|
11
12
|
|
|
@@ -53,4 +54,10 @@ program
|
|
|
53
54
|
.option('--show', 'show current config')
|
|
54
55
|
.action(configCommand);
|
|
55
56
|
|
|
57
|
+
program
|
|
58
|
+
.command('setup')
|
|
59
|
+
.description('Full Jarvis install ā Ollama + llama3.2 + Claude Code hooks (one command)')
|
|
60
|
+
.option('-y, --yes', 'skip confirmation prompt')
|
|
61
|
+
.action(setup);
|
|
62
|
+
|
|
56
63
|
program.parse();
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -23,7 +23,8 @@ const BINARY_EXTS = new Set([
|
|
|
23
23
|
|
|
24
24
|
const SKIP_FILES = new Set([
|
|
25
25
|
'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb',
|
|
26
|
-
'.env', '.env.local', '.env.production', '.DS_Store'
|
|
26
|
+
'.env', '.env.local', '.env.production', '.DS_Store',
|
|
27
|
+
'CLAUDE.md', 'jarvis-map.txt', 'PRODUCT_AUDIT.md'
|
|
27
28
|
]);
|
|
28
29
|
|
|
29
30
|
// Entry point patterns ā ordered by priority
|
package/src/setup.js
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// devjar setup ā full Jarvis system installer
|
|
2
|
+
// Installs Ollama, pulls model, configures Claude Code hooks
|
|
3
|
+
// One command from zero to fully working Jarvis + STAR-C
|
|
4
|
+
|
|
5
|
+
import { execSync, spawn } from 'child_process';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import https from 'https';
|
|
10
|
+
import readline from 'readline';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { saveConfig } from './providers/index.js';
|
|
13
|
+
|
|
14
|
+
const HOME = os.homedir();
|
|
15
|
+
const CLAUDE_DIR = path.join(HOME, '.claude');
|
|
16
|
+
const SCRIPTS_DIR = path.join(CLAUDE_DIR, 'scripts');
|
|
17
|
+
const SETTINGS_FILE = path.join(CLAUDE_DIR, 'settings.json');
|
|
18
|
+
const DEVJAR_DIR = path.join(HOME, '.devjar');
|
|
19
|
+
const MODEL = 'llama3.2';
|
|
20
|
+
|
|
21
|
+
// āā Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
22
|
+
|
|
23
|
+
function step(msg) {
|
|
24
|
+
process.stdout.write(`\n${chalk.bold.blue('ā')} ${chalk.white(msg)}\n`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function ok(msg) {
|
|
28
|
+
process.stdout.write(` ${chalk.green('ā')} ${chalk.white(msg)}\n`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function warn(msg) {
|
|
32
|
+
process.stdout.write(` ${chalk.yellow('ā ')} ${chalk.gray(msg)}\n`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fail(msg) {
|
|
36
|
+
process.stdout.write(` ${chalk.red('ā')} ${msg}\n`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function spinner(msg) {
|
|
40
|
+
const frames = ['ā ','ā ','ā ¹','ā ø','ā ¼','ā “','ā ¦','ā §','ā ','ā '];
|
|
41
|
+
let i = 0;
|
|
42
|
+
const id = setInterval(() => process.stdout.write(`\r ${chalk.cyan(frames[i++ % frames.length])} ${chalk.yellow(msg)}`), 80);
|
|
43
|
+
return () => { clearInterval(id); process.stdout.write('\r' + ' '.repeat(60) + '\r'); };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function run(cmd, opts = {}) {
|
|
47
|
+
try { execSync(cmd, { stdio: 'pipe', ...opts }); return true; }
|
|
48
|
+
catch { return false; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function runOut(cmd) {
|
|
52
|
+
try { return execSync(cmd, { stdio: 'pipe' }).toString().trim(); }
|
|
53
|
+
catch { return ''; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ask(question) {
|
|
57
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
58
|
+
return new Promise(resolve => rl.question(question, ans => { rl.close(); resolve(ans.trim()); }));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ollamaRunning() {
|
|
62
|
+
try {
|
|
63
|
+
const res = execSync('curl -s http://localhost:11434/api/tags', { stdio: 'pipe' }).toString();
|
|
64
|
+
return res.includes('models');
|
|
65
|
+
} catch { return false; }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function ollamaInstalled() {
|
|
69
|
+
return !!runOut('ollama --version') || !!runOut('where ollama') || !!runOut('which ollama');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// āā Step 1: Install Ollama āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
73
|
+
|
|
74
|
+
async function installOllama() {
|
|
75
|
+
step('Checking Ollama...');
|
|
76
|
+
|
|
77
|
+
if (ollamaInstalled()) { ok('Ollama already installed'); return true; }
|
|
78
|
+
|
|
79
|
+
const platform = process.platform;
|
|
80
|
+
|
|
81
|
+
if (platform === 'win32') {
|
|
82
|
+
// Try winget first
|
|
83
|
+
const hasWinget = run('winget --version');
|
|
84
|
+
if (hasWinget) {
|
|
85
|
+
const stop = spinner('Installing Ollama via winget...');
|
|
86
|
+
const ok2 = run('winget install Ollama.Ollama --accept-source-agreements --accept-package-agreements --silent');
|
|
87
|
+
stop();
|
|
88
|
+
if (ok2) { ok('Ollama installed via winget'); return true; }
|
|
89
|
+
}
|
|
90
|
+
// Fallback: direct download
|
|
91
|
+
warn('winget failed ā download manually: https://ollama.com/download/OllamaSetup.exe');
|
|
92
|
+
return false;
|
|
93
|
+
|
|
94
|
+
} else if (platform === 'darwin') {
|
|
95
|
+
const hasBrew = run('brew --version');
|
|
96
|
+
if (hasBrew) {
|
|
97
|
+
const stop = spinner('Installing Ollama via brew...');
|
|
98
|
+
const ok2 = run('brew install ollama');
|
|
99
|
+
stop();
|
|
100
|
+
if (ok2) { ok('Ollama installed via brew'); return true; }
|
|
101
|
+
}
|
|
102
|
+
warn('Install manually: https://ollama.com/download');
|
|
103
|
+
return false;
|
|
104
|
+
|
|
105
|
+
} else {
|
|
106
|
+
// Linux
|
|
107
|
+
const stop = spinner('Installing Ollama via install script...');
|
|
108
|
+
const ok2 = run('curl -fsSL https://ollama.com/install.sh | sh');
|
|
109
|
+
stop();
|
|
110
|
+
if (ok2) { ok('Ollama installed'); return true; }
|
|
111
|
+
warn('Install manually: https://ollama.com/download');
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// āā Step 2: Start Ollama āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
117
|
+
|
|
118
|
+
async function startOllama() {
|
|
119
|
+
step('Starting Ollama service...');
|
|
120
|
+
|
|
121
|
+
if (ollamaRunning()) { ok('Ollama already running'); return; }
|
|
122
|
+
|
|
123
|
+
// Start in background
|
|
124
|
+
const child = spawn('ollama', ['serve'], {
|
|
125
|
+
detached: true, stdio: 'ignore',
|
|
126
|
+
windowsHide: true,
|
|
127
|
+
});
|
|
128
|
+
child.unref();
|
|
129
|
+
|
|
130
|
+
// Wait up to 5s for it to start
|
|
131
|
+
const stop = spinner('Waiting for Ollama to start...');
|
|
132
|
+
for (let i = 0; i < 10; i++) {
|
|
133
|
+
await new Promise(r => setTimeout(r, 500));
|
|
134
|
+
if (ollamaRunning()) { stop(); ok('Ollama service started'); return; }
|
|
135
|
+
}
|
|
136
|
+
stop();
|
|
137
|
+
warn('Ollama not responding ā you may need to run `ollama serve` manually');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// āā Step 3: Add to startup āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
141
|
+
|
|
142
|
+
function addToStartup() {
|
|
143
|
+
step('Adding Ollama to startup...');
|
|
144
|
+
|
|
145
|
+
if (process.platform === 'win32') {
|
|
146
|
+
const startupDir = path.join(HOME, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
147
|
+
const shortcut = path.join(startupDir, 'OllamaServe.lnk');
|
|
148
|
+
if (fs.existsSync(shortcut)) { ok('Already in startup'); return; }
|
|
149
|
+
|
|
150
|
+
const ps = `
|
|
151
|
+
$startup = "${startupDir.replace(/\\/g, '\\\\')}";
|
|
152
|
+
$s = (New-Object -ComObject WScript.Shell).CreateShortcut("$startup\\OllamaServe.lnk");
|
|
153
|
+
$s.TargetPath = "ollama"; $s.Arguments = "serve"; $s.Save()`;
|
|
154
|
+
const ok2 = run(`powershell -Command "${ps.replace(/\n/g, ' ')}"`);
|
|
155
|
+
if (ok2) ok('Added to Windows startup'); else warn('Could not add to startup ā run `ollama serve` manually each session');
|
|
156
|
+
|
|
157
|
+
} else if (process.platform === 'darwin') {
|
|
158
|
+
const plist = path.join(HOME, 'Library', 'LaunchAgents', 'com.ollama.serve.plist');
|
|
159
|
+
if (!fs.existsSync(plist)) {
|
|
160
|
+
fs.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
|
|
161
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
162
|
+
<plist version="1.0"><dict>
|
|
163
|
+
<key>Label</key><string>com.ollama.serve</string>
|
|
164
|
+
<key>ProgramArguments</key><array><string>ollama</string><string>serve</string></array>
|
|
165
|
+
<key>RunAtLoad</key><true/>
|
|
166
|
+
</dict></plist>`);
|
|
167
|
+
run('launchctl load ~/Library/LaunchAgents/com.ollama.serve.plist');
|
|
168
|
+
}
|
|
169
|
+
ok('Added to macOS LaunchAgents');
|
|
170
|
+
|
|
171
|
+
} else {
|
|
172
|
+
// Linux systemd user service
|
|
173
|
+
const serviceDir = path.join(HOME, '.config', 'systemd', 'user');
|
|
174
|
+
fs.mkdirSync(serviceDir, { recursive: true });
|
|
175
|
+
fs.writeFileSync(path.join(serviceDir, 'ollama.service'),
|
|
176
|
+
`[Unit]\nDescription=Ollama\n[Service]\nExecStart=ollama serve\nRestart=always\n[Install]\nWantedBy=default.target\n`);
|
|
177
|
+
run('systemctl --user enable ollama');
|
|
178
|
+
run('systemctl --user start ollama');
|
|
179
|
+
ok('Added to systemd user services');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// āā Step 4: Pull model āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
184
|
+
|
|
185
|
+
async function pullModel() {
|
|
186
|
+
step(`Pulling ${MODEL} model...`);
|
|
187
|
+
|
|
188
|
+
// Check if already pulled
|
|
189
|
+
const tags = runOut('ollama list');
|
|
190
|
+
if (tags.includes(MODEL)) { ok(`${MODEL} already pulled`); return true; }
|
|
191
|
+
|
|
192
|
+
const stop = spinner(`Downloading ${MODEL} (~2GB, this takes a few minutes)...`);
|
|
193
|
+
const ok2 = run(`ollama pull ${MODEL}`, { timeout: 600000 });
|
|
194
|
+
stop();
|
|
195
|
+
if (ok2) { ok(`${MODEL} ready`); return true; }
|
|
196
|
+
fail(`Failed to pull ${MODEL} ā run: ollama pull ${MODEL}`);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// āā Step 5: Write prompt-normalizer.js āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
201
|
+
|
|
202
|
+
function writeNormalizer() {
|
|
203
|
+
step('Installing prompt normalizer...');
|
|
204
|
+
fs.mkdirSync(SCRIPTS_DIR, { recursive: true });
|
|
205
|
+
|
|
206
|
+
const normalizerPath = path.join(SCRIPTS_DIR, 'prompt-normalizer.js');
|
|
207
|
+
const content = `#!/usr/bin/env node
|
|
208
|
+
import http from 'http';
|
|
209
|
+
const OLLAMA_URL = 'http://localhost:11434';
|
|
210
|
+
const MODEL = '${MODEL}';
|
|
211
|
+
function done(ctx = '') {
|
|
212
|
+
process.stdout.write(JSON.stringify({continue:true,hookSpecificOutput:{hookEventName:'UserPromptSubmit',additionalContext:ctx}}));
|
|
213
|
+
process.exit(0);
|
|
214
|
+
}
|
|
215
|
+
let raw = '';
|
|
216
|
+
process.stdin.setEncoding('utf8');
|
|
217
|
+
process.stdin.on('data', c => { raw += c; });
|
|
218
|
+
process.stdin.on('end', () => {
|
|
219
|
+
const input = (() => { try { return JSON.parse(raw); } catch { return {}; } })();
|
|
220
|
+
const userPrompt = (input?.tool_input?.prompt || input?.prompt || '').slice(0, 800);
|
|
221
|
+
if (!userPrompt) return done();
|
|
222
|
+
const system = \`Normalize this dev prompt into STAR-C JSON. Rules: always interpret charitably, conversational=discuss action. Output ONLY JSON: {"s":"...","t":"...","a":"fix|add|explain|refactor|deploy|debug|discuss","r":"...","c":"...","complexity":"haiku|sonnet|opus","reason":"one line"}\`;
|
|
223
|
+
const body = JSON.stringify({model:MODEL,system,prompt:userPrompt,stream:false,options:{num_predict:150,temperature:0.1}});
|
|
224
|
+
const req = http.request(OLLAMA_URL+'/api/generate',{method:'POST',headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(body)},timeout:10000},(res)=>{
|
|
225
|
+
let d=''; res.on('data',c=>{d+=c;}); res.on('end',()=>{
|
|
226
|
+
try {
|
|
227
|
+
const t=JSON.parse(d).response?.trim()||'';
|
|
228
|
+
const p=JSON.parse(t.slice(t.indexOf('{'),t.lastIndexOf('}')+1));
|
|
229
|
+
const ctx=[\`[STAR-C]\`,\`S: \${p.s}\`,\`T: \${p.t}\`,\`A: \${p.a}\`,\`R: \${p.r}\`,p.c?\`C: \${p.c}\`:null,\`[COMPLEXITY]: \${p.complexity} ā \${p.reason}\`].filter(Boolean).join('\\n');
|
|
230
|
+
done(ctx);
|
|
231
|
+
} catch { done(); }
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
req.on('error',()=>done()); req.on('timeout',()=>{req.destroy();done();});
|
|
235
|
+
req.write(body); req.end();
|
|
236
|
+
});
|
|
237
|
+
`;
|
|
238
|
+
fs.writeFileSync(normalizerPath, content, 'utf8');
|
|
239
|
+
ok(`prompt-normalizer.js ā ${normalizerPath}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// āā Step 6: Write session-loader.js āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
243
|
+
|
|
244
|
+
function writeSessionLoader() {
|
|
245
|
+
const loaderPath = path.join(SCRIPTS_DIR, 'session-loader.js');
|
|
246
|
+
if (fs.existsSync(loaderPath)) { ok('session-loader.js already exists'); return; }
|
|
247
|
+
|
|
248
|
+
const content = `#!/usr/bin/env node
|
|
249
|
+
import fs from 'fs'; import path from 'path'; import os from 'os';
|
|
250
|
+
const MAPS_DIR = path.join(os.homedir(), '.claude', 'scripts');
|
|
251
|
+
const PROJECT_MAPS = { 'care-home': path.join(MAPS_DIR, 'care-home-map.txt') };
|
|
252
|
+
function detect() {
|
|
253
|
+
const cwd = process.cwd().toLowerCase();
|
|
254
|
+
for (const [k,v] of Object.entries(PROJECT_MAPS)) { if (cwd.includes(k)) return v; }
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const f = detect();
|
|
259
|
+
if (!f || !fs.existsSync(f)) { process.exit(0); }
|
|
260
|
+
const map = fs.readFileSync(f, 'utf8');
|
|
261
|
+
process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:'SessionStart',additionalContext:'=== JARVIS PROJECT KNOWLEDGE ===\\nDO NOT re-read files unless making targeted edits.\\n\\n'+map+'\\n=== END ==='}}));
|
|
262
|
+
} catch { process.exit(0); }
|
|
263
|
+
`;
|
|
264
|
+
fs.writeFileSync(loaderPath, content, 'utf8');
|
|
265
|
+
ok(`session-loader.js ā ${loaderPath}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// āā Step 7: Patch Claude Code settings.json āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
269
|
+
|
|
270
|
+
function patchClaudeSettings() {
|
|
271
|
+
step('Configuring Claude Code hooks...');
|
|
272
|
+
fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
273
|
+
|
|
274
|
+
let settings = {};
|
|
275
|
+
if (fs.existsSync(SETTINGS_FILE)) {
|
|
276
|
+
try { settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8')); } catch {}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
settings.hooks = settings.hooks || {};
|
|
280
|
+
|
|
281
|
+
// SessionStart hook
|
|
282
|
+
if (!settings.hooks.SessionStart) {
|
|
283
|
+
settings.hooks.SessionStart = [{ hooks: [{ type: 'command', command: `node ${SCRIPTS_DIR.replace(/\\/g, '/')}/session-loader.js`, statusMessage: 'Loading project knowledge...' }] }];
|
|
284
|
+
ok('SessionStart hook added');
|
|
285
|
+
} else { ok('SessionStart hook already configured'); }
|
|
286
|
+
|
|
287
|
+
// UserPromptSubmit hook
|
|
288
|
+
if (!settings.hooks.UserPromptSubmit) {
|
|
289
|
+
settings.hooks.UserPromptSubmit = [{ hooks: [{ type: 'command', command: `node ${SCRIPTS_DIR.replace(/\\/g, '/')}/prompt-normalizer.js`, statusMessage: 'Structuring prompt...' }] }];
|
|
290
|
+
ok('UserPromptSubmit hook added');
|
|
291
|
+
} else { ok('UserPromptSubmit hook already configured'); }
|
|
292
|
+
|
|
293
|
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf8');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// āā Step 8: Save devjar config āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
297
|
+
|
|
298
|
+
function saveDevjarConfig() {
|
|
299
|
+
step('Saving devjar config...');
|
|
300
|
+
fs.mkdirSync(DEVJAR_DIR, { recursive: true });
|
|
301
|
+
saveConfig({ provider: 'ollama', model: MODEL });
|
|
302
|
+
ok(`Provider set: ollama / ${MODEL}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// āā Main Export āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
306
|
+
|
|
307
|
+
export async function setup(options = {}) {
|
|
308
|
+
console.log(chalk.bold.blue('\n⬔ devjar setup') + chalk.gray(' ā Jarvis full install\n'));
|
|
309
|
+
console.log(chalk.gray('ā'.repeat(52)));
|
|
310
|
+
console.log(chalk.gray('This will install Ollama, pull llama3.2, and configure'));
|
|
311
|
+
console.log(chalk.gray('Claude Code hooks for STAR-C prompt normalization.\n'));
|
|
312
|
+
|
|
313
|
+
if (!options.yes) {
|
|
314
|
+
const ans = await ask(chalk.bold('Continue?') + chalk.gray(' (Y/n) '));
|
|
315
|
+
if (ans.toLowerCase() === 'n') { console.log(chalk.gray('Cancelled.')); return; }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const installed = await installOllama();
|
|
319
|
+
if (installed) {
|
|
320
|
+
await startOllama();
|
|
321
|
+
addToStartup();
|
|
322
|
+
await pullModel();
|
|
323
|
+
} else {
|
|
324
|
+
warn('Skipping Ollama steps ā install manually then re-run `devjar setup`');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
writeNormalizer();
|
|
328
|
+
writeSessionLoader();
|
|
329
|
+
patchClaudeSettings();
|
|
330
|
+
saveDevjarConfig();
|
|
331
|
+
|
|
332
|
+
console.log(chalk.gray('\nā'.repeat(52)));
|
|
333
|
+
console.log(chalk.bold.green('\nā Jarvis system fully configured!\n'));
|
|
334
|
+
console.log(chalk.white(' Every Claude Code session now has:'));
|
|
335
|
+
console.log(chalk.gray(' ⢠Project knowledge injected at start (SessionStart)'));
|
|
336
|
+
console.log(chalk.gray(' ⢠Prompts structured via Ollama/llama3.2 (UserPromptSubmit)'));
|
|
337
|
+
console.log(chalk.gray(' ⢠Zero API keys, zero cost, fully local\n'));
|
|
338
|
+
console.log(chalk.gray(' Next: cd into your project and run ') + chalk.cyan('devjar init'));
|
|
339
|
+
console.log(chalk.gray(' Then open Claude Code ā Jarvis activates automatically.\n'));
|
|
340
|
+
}
|