clawkit-ai 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.
Files changed (41) hide show
  1. package/PRD.md +416 -0
  2. package/README.md +156 -0
  3. package/bin/clawkit.mjs +57 -0
  4. package/dashboard/app.js +287 -0
  5. package/dashboard/index.html +137 -0
  6. package/dashboard/style.css +121 -0
  7. package/package.json +14 -0
  8. package/skills/code-mentor/SKILL.md +85 -0
  9. package/skills/csv-pipeline/SKILL.md +82 -0
  10. package/skills/developer/SKILL.md +57 -0
  11. package/skills/image/SKILL.md +71 -0
  12. package/skills/json/SKILL.md +59 -0
  13. package/skills/productivity/SKILL.md +68 -0
  14. package/skills/quick-reminders/SKILL.md +70 -0
  15. package/skills/svg-draw/SKILL.md +75 -0
  16. package/skills/weather/SKILL.md +72 -0
  17. package/skills/workspace-manager/SKILL.md +69 -0
  18. package/src/cron.mjs +30 -0
  19. package/src/dashboard.mjs +69 -0
  20. package/src/doctor.mjs +69 -0
  21. package/src/init.mjs +137 -0
  22. package/src/scaffold.mjs +57 -0
  23. package/src/skills.mjs +52 -0
  24. package/src/status.mjs +43 -0
  25. package/src/update.mjs +100 -0
  26. package/src/upgrade.mjs +104 -0
  27. package/src/utils.mjs +67 -0
  28. package/src/validate.mjs +66 -0
  29. package/templates/AGENTS.md +77 -0
  30. package/templates/HEARTBEAT.md +11 -0
  31. package/templates/IDENTITY.md +7 -0
  32. package/templates/LEARNINGS.md +14 -0
  33. package/templates/MEMORY.md +14 -0
  34. package/templates/PROTOCOL_COST_EFFICIENCY.md +30 -0
  35. package/templates/SKILLS-INDEX.md +21 -0
  36. package/templates/SOUL.md +22 -0
  37. package/templates/TOOLS.md +14 -0
  38. package/templates/USER.md +5 -0
  39. package/templates/memory/knowledge/about-me.md +13 -0
  40. package/templates/scripts/nightly-consolidation.sh +54 -0
  41. package/templates/tasks/QUEUE.md +10 -0
@@ -0,0 +1,69 @@
1
+ import { createServer } from 'http';
2
+ import { readFileSync, existsSync } from 'fs';
3
+ import { join, extname } from 'path';
4
+ import { execSync } from 'child_process';
5
+ import { fileURLToPath } from 'url';
6
+ import { c, log } from './utils.mjs';
7
+
8
+ const MIME = {
9
+ '.html': 'text/html',
10
+ '.css': 'text/css',
11
+ '.js': 'application/javascript',
12
+ '.json': 'application/json',
13
+ '.png': 'image/png',
14
+ '.svg': 'image/svg+xml',
15
+ };
16
+
17
+ export function runDashboard(flags) {
18
+ const port = parseInt(flags.port) || 8080;
19
+ const thisDir = fileURLToPath(new URL('.', import.meta.url));
20
+ const dashDir = join(thisDir, '..', 'dashboard');
21
+
22
+ if (!existsSync(join(dashDir, 'index.html'))) {
23
+ log.err('Dashboard files not found. Reinstall ClawKit.');
24
+ process.exit(1);
25
+ }
26
+
27
+ const server = createServer((req, res) => {
28
+ let filePath = req.url === '/' ? '/index.html' : req.url;
29
+ filePath = filePath.split('?')[0]; // strip query
30
+ const full = join(dashDir, filePath);
31
+
32
+ if (!existsSync(full)) {
33
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
34
+ res.end('Not found');
35
+ return;
36
+ }
37
+
38
+ const ext = extname(full);
39
+ const mime = MIME[ext] || 'application/octet-stream';
40
+ try {
41
+ const data = readFileSync(full);
42
+ res.writeHead(200, { 'Content-Type': mime });
43
+ res.end(data);
44
+ } catch {
45
+ res.writeHead(500);
46
+ res.end('Error');
47
+ }
48
+ });
49
+
50
+ server.listen(port, () => {
51
+ const url = `http://localhost:${port}`;
52
+ console.log(`\n ${c.green}⚡${c.reset} Dashboard running at ${c.cyan}${url}${c.reset}\n`);
53
+ console.log(` ${c.dim}Press Ctrl+C to stop${c.reset}\n`);
54
+
55
+ // Open browser
56
+ try {
57
+ const platform = process.platform;
58
+ if (platform === 'darwin') execSync(`open ${url}`);
59
+ else if (platform === 'win32') execSync(`start ${url}`);
60
+ else execSync(`xdg-open ${url} 2>/dev/null || true`);
61
+ } catch {}
62
+ });
63
+
64
+ process.on('SIGINT', () => {
65
+ console.log(`\n ${c.dim}Shutting down...${c.reset}`);
66
+ server.close();
67
+ process.exit(0);
68
+ });
69
+ }
package/src/doctor.mjs ADDED
@@ -0,0 +1,69 @@
1
+ import { existsSync, readFileSync, readdirSync, accessSync, constants } from 'fs';
2
+ import { join } from 'path';
3
+ import { c, log, detectWorkspace, cmdExists } from './utils.mjs';
4
+
5
+ export function runDoctor() {
6
+ const checks = [];
7
+ const pass = (name) => checks.push({ name, ok: true });
8
+ const fail = (name, hint) => checks.push({ name, ok: false, hint });
9
+
10
+ // 1. OpenClaw installed
11
+ cmdExists('openclaw') ? pass('OpenClaw CLI installed') : fail('OpenClaw CLI installed', 'Install: npm i -g openclaw');
12
+
13
+ // 2. Workspace
14
+ const workspace = detectWorkspace();
15
+ if (workspace && existsSync(workspace)) {
16
+ pass('Workspace directory exists');
17
+ } else {
18
+ fail('Workspace directory exists', 'Set CLAWKIT_WORKSPACE env var');
19
+ }
20
+
21
+ if (workspace) {
22
+ // 3. AGENTS.md
23
+ existsSync(join(workspace, 'AGENTS.md')) ? pass('AGENTS.md present') : fail('AGENTS.md present', 'Run: clawkit init');
24
+
25
+ // 4. Config
26
+ const cfgPath = join(workspace, 'skills', '.clawkit', 'config.json');
27
+ if (existsSync(cfgPath)) {
28
+ try { JSON.parse(readFileSync(cfgPath, 'utf8')); pass('Config valid'); }
29
+ catch { fail('Config valid', 'config.json is corrupt — re-run clawkit init'); }
30
+ } else {
31
+ fail('Config valid', 'Run: clawkit init');
32
+ }
33
+
34
+ // 5. Skills
35
+ const sd = join(workspace, 'skills');
36
+ if (existsSync(sd)) {
37
+ const cnt = readdirSync(sd, { withFileTypes: true }).filter(d => d.isDirectory() && !d.name.startsWith('.')).length;
38
+ cnt > 0 ? pass(`Skills installed (${cnt})`) : fail('Skills installed', 'Run: clawkit init');
39
+ } else {
40
+ fail('Skills installed', 'Run: clawkit init');
41
+ }
42
+
43
+ // 6. Consolidation script
44
+ const cs = join(workspace, 'scripts/nightly-consolidation.sh');
45
+ if (existsSync(cs)) {
46
+ try { accessSync(cs, constants.X_OK); pass('Consolidation script executable'); }
47
+ catch { fail('Consolidation script executable', `Run: chmod +x ${cs}`); }
48
+ } else {
49
+ fail('Consolidation script present', 'Run: clawkit init');
50
+ }
51
+
52
+ // 7. Memory dirs
53
+ existsSync(join(workspace, 'memory/daily')) && existsSync(join(workspace, 'memory/knowledge'))
54
+ ? pass('Memory directories') : fail('Memory directories', 'Run: clawkit init');
55
+ }
56
+
57
+ // 5. API key
58
+ process.env.CLAWKIT_API_KEY ? pass('API key set') : fail('API key set', 'Set CLAWKIT_API_KEY env var');
59
+
60
+ console.log(`\n${c.bold}${c.cyan}🩺 ClawKit Doctor${c.reset}\n`);
61
+ for (const ch of checks) {
62
+ const icon = ch.ok ? `${c.green}✔${c.reset}` : `${c.red}✖${c.reset}`;
63
+ console.log(` ${icon} ${ch.name}`);
64
+ if (!ch.ok && ch.hint) console.log(` ${c.dim}→ ${ch.hint}${c.reset}`);
65
+ }
66
+
67
+ const passed = checks.filter(c => c.ok).length;
68
+ console.log(`\n ${passed}/${checks.length} checks passed\n`);
69
+ }
package/src/init.mjs ADDED
@@ -0,0 +1,137 @@
1
+ import { createInterface } from 'readline';
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { c, log, detectWorkspace, detectTimezone, today } from './utils.mjs';
5
+ import { readApiKey, parseTier, validateKey } from './validate.mjs';
6
+ import { scaffoldWorkspace, backupWorkspace } from './scaffold.mjs';
7
+ import { installSkills } from './skills.mjs';
8
+
9
+ function ask(rl, question, defaultVal) {
10
+ return new Promise((resolve) => {
11
+ rl.question(`${c.cyan}?${c.reset} ${question} ${c.dim}(${defaultVal})${c.reset} `, (answer) => {
12
+ resolve(answer.trim() || defaultVal);
13
+ });
14
+ });
15
+ }
16
+
17
+ export async function runInit(flags) {
18
+ const yes = flags.yes || flags.y;
19
+ const force = flags.force;
20
+
21
+ console.log(`\n${c.bold}${c.magenta}🧰 ClawKit Init${c.reset}\n`);
22
+
23
+ // Step 1: Detect workspace
24
+ log.step(1, 'Detecting workspace...');
25
+ let workspace = detectWorkspace();
26
+
27
+ const rl = !yes ? createInterface({ input: process.stdin, output: process.stdout }) : null;
28
+
29
+ if (!workspace && !yes) {
30
+ workspace = await ask(rl, 'Workspace path:', join(process.env.HOME || '~', '.openclaw', 'workspace'));
31
+ }
32
+ if (!workspace) {
33
+ workspace = join(process.env.HOME || '/tmp', '.openclaw', 'workspace');
34
+ }
35
+ mkdirSync(workspace, { recursive: true });
36
+ log.ok(`Workspace: ${workspace}`);
37
+
38
+ // Step 2: Check existing
39
+ log.step(2, 'Checking existing files...');
40
+ if (existsSync(join(workspace, 'AGENTS.md'))) {
41
+ if (!yes && !force) {
42
+ const overwrite = await ask(rl, 'Existing workspace detected. Backup & overwrite? (y/n):', 'y');
43
+ if (overwrite.toLowerCase() !== 'y') {
44
+ log.info('Aborted.');
45
+ rl?.close();
46
+ return;
47
+ }
48
+ }
49
+ const bk = backupWorkspace(workspace);
50
+ log.ok(`Backed up to ${bk}`);
51
+ } else {
52
+ log.ok('Clean workspace');
53
+ }
54
+
55
+ // Step 3: Read API key
56
+ log.step(3, 'Reading API key...');
57
+ let apiKey = readApiKey();
58
+ if (!apiKey && !yes) {
59
+ apiKey = await ask(rl, 'Enter your ClawKit API key (or press enter to skip):', '');
60
+ }
61
+ const tier = parseTier(apiKey);
62
+ if (apiKey) {
63
+ log.ok(`API key found (tier: ${tier})`);
64
+ } else {
65
+ log.warn('No API key — using free tier');
66
+ }
67
+
68
+ // Step 4: Validate
69
+ log.step(4, 'Validating...');
70
+ const validation = await validateKey(apiKey);
71
+ if (!validation.valid && !validation.offline) {
72
+ log.err(`Validation failed: ${validation.error}`);
73
+ log.info('Continuing with free tier...');
74
+ } else if (validation.offline) {
75
+ log.warn('Offline mode — continuing with key prefix tier');
76
+ } else {
77
+ log.ok('API key validated');
78
+ }
79
+
80
+ const effectiveTier = validation.tier || tier;
81
+
82
+ // Step 5: Prompts
83
+ log.step(5, 'Configuration...');
84
+ const agentName = yes ? 'Atlas' : await ask(rl, 'What should your agent be called?', 'Atlas');
85
+ const userName = yes ? 'Human' : await ask(rl, "What's your name?", 'Human');
86
+ const timezone = yes ? detectTimezone() : await ask(rl, 'Your timezone?', detectTimezone());
87
+
88
+ rl?.close();
89
+
90
+ const vars = { agentName, userName, timezone, tier: effectiveTier, date: today() };
91
+
92
+ // Step 6: Scaffold
93
+ log.step(6, 'Scaffolding workspace...');
94
+ scaffoldWorkspace(workspace, vars);
95
+ log.ok('Template files written');
96
+
97
+ // Step 7: Skills
98
+ log.step(7, 'Installing skills...');
99
+ const skillCount = installSkills(workspace, effectiveTier);
100
+
101
+ // Step 8: Config
102
+ log.step(8, 'Writing config...');
103
+ const configDir = join(workspace, 'skills', '.clawkit');
104
+ mkdirSync(configDir, { recursive: true });
105
+ const config = {
106
+ version: '1.0.0',
107
+ tier: effectiveTier,
108
+ apiKey: apiKey || '',
109
+ installedAt: new Date().toISOString(),
110
+ agentName,
111
+ userName,
112
+ timezone,
113
+ features: validation.features || {},
114
+ };
115
+ writeFileSync(join(configDir, 'config.json'), JSON.stringify(config, null, 2), 'utf8');
116
+ log.ok('Config saved');
117
+
118
+ // Step 9: Success
119
+ console.log(`
120
+ ${c.green}╔══════════════════════════════════════════════╗
121
+ ║ 🧰 ClawKit initialized successfully! ║
122
+ ╠══════════════════════════════════════════════╣
123
+ ║ ║
124
+ ║ Agent: ${(agentName + ' ').slice(0, 36)}║
125
+ ║ Tier: ${(effectiveTier + ' ').slice(0, 37)}║
126
+ ║ Skills: ${(skillCount + ' installed ').slice(0, 35)}║
127
+ ║ Memory: 3-layer system active ║
128
+ ║ ║
129
+ ║ Next steps: ║
130
+ ║ 1. openclaw gateway restart ║
131
+ ║ 2. Start chatting — your agent is ready! ║
132
+ ║ ║
133
+ ║ Run clawkit status to check health ║
134
+ ║ Run clawkit upgrade to go Pro ║
135
+ ╚══════════════════════════════════════════════╝${c.reset}
136
+ `);
137
+ }
@@ -0,0 +1,57 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync, chmodSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const PKG_ROOT = join(__dirname, '..');
7
+
8
+ export function replaceVars(text, vars) {
9
+ return text
10
+ .replace(/\{\{AGENT_NAME\}\}/g, vars.agentName || 'Atlas')
11
+ .replace(/\{\{USER_NAME\}\}/g, vars.userName || 'Human')
12
+ .replace(/\{\{TIMEZONE\}\}/g, vars.timezone || 'UTC')
13
+ .replace(/\{\{TIER\}\}/g, vars.tier || 'free')
14
+ .replace(/\{\{DATE\}\}/g, vars.date || new Date().toISOString().slice(0, 10));
15
+ }
16
+
17
+ const TEMPLATE_FILES = [
18
+ 'AGENTS.md', 'SOUL.md', 'IDENTITY.md', 'USER.md', 'TOOLS.md', 'HEARTBEAT.md',
19
+ 'MEMORY.md', 'LEARNINGS.md', 'PROTOCOL_COST_EFFICIENCY.md', 'SKILLS-INDEX.md',
20
+ 'memory/knowledge/about-me.md', 'tasks/QUEUE.md', 'scripts/nightly-consolidation.sh',
21
+ ];
22
+
23
+ export function scaffoldWorkspace(workspace, vars) {
24
+ for (const d of ['memory/daily', 'memory/knowledge', 'memory/para', 'tasks', 'scripts', 'skills']) {
25
+ mkdirSync(join(workspace, d), { recursive: true });
26
+ }
27
+
28
+ for (const f of TEMPLATE_FILES) {
29
+ const src = join(PKG_ROOT, 'templates', f);
30
+ const dest = join(workspace, f);
31
+ mkdirSync(dirname(dest), { recursive: true });
32
+ const content = readFileSync(src, 'utf8');
33
+ writeFileSync(dest, replaceVars(content, vars), 'utf8');
34
+ }
35
+
36
+ // Make consolidation script executable
37
+ const scriptPath = join(workspace, 'scripts/nightly-consolidation.sh');
38
+ if (existsSync(scriptPath)) {
39
+ chmodSync(scriptPath, 0o755);
40
+ }
41
+ }
42
+
43
+ export function backupWorkspace(workspace) {
44
+ const backupDir = join(workspace, '.clawkit-backup');
45
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
46
+ const dest = join(backupDir, ts);
47
+ mkdirSync(dest, { recursive: true });
48
+ for (const f of TEMPLATE_FILES) {
49
+ const src = join(workspace, f);
50
+ if (existsSync(src)) {
51
+ const d = join(dest, f);
52
+ mkdirSync(dirname(d), { recursive: true });
53
+ cpSync(src, d);
54
+ }
55
+ }
56
+ return dest;
57
+ }
package/src/skills.mjs ADDED
@@ -0,0 +1,52 @@
1
+ import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { execSync } from 'child_process';
5
+ import { log, cmdExists } from './utils.mjs';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const PKG_ROOT = join(__dirname, '..');
9
+ const BUNDLED_DIR = join(PKG_ROOT, 'skills');
10
+
11
+ const PRO_SKILLS = [
12
+ 'reminder', 'data-analyst', 'automation-workflows', 'sales',
13
+ 'security-monitor', 'nano-pdf', 'pdf-to-structured',
14
+ ];
15
+
16
+ export function installSkills(workspace, tier) {
17
+ const skillsDir = join(workspace, 'skills');
18
+ mkdirSync(skillsDir, { recursive: true });
19
+
20
+ // Install bundled free skills
21
+ let count = 0;
22
+ const entries = readdirSync(BUNDLED_DIR, { withFileTypes: true });
23
+ for (const entry of entries) {
24
+ if (!entry.isDirectory()) continue;
25
+ const srcSkill = join(BUNDLED_DIR, entry.name, 'SKILL.md');
26
+ if (!existsSync(srcSkill)) continue;
27
+ const destDir = join(skillsDir, entry.name);
28
+ mkdirSync(destDir, { recursive: true });
29
+ const content = readFileSync(srcSkill, 'utf8');
30
+ writeFileSync(join(destDir, 'SKILL.md'), content, 'utf8');
31
+ count++;
32
+ }
33
+ log.ok(`Installed ${count} free-tier skills`);
34
+
35
+ // Pro skills via clawhub
36
+ if (tier === 'pro') {
37
+ if (cmdExists('clawhub')) {
38
+ for (const slug of PRO_SKILLS) {
39
+ try {
40
+ execSync(`clawhub install ${slug} --force`, { cwd: workspace, stdio: 'ignore', timeout: 30000 });
41
+ log.ok(`Installed pro skill: ${slug}`);
42
+ } catch {
43
+ log.warn(`Could not install pro skill: ${slug}`);
44
+ }
45
+ }
46
+ } else {
47
+ log.warn('clawhub CLI not found — skipping pro skill installation. Run: npm i -g clawhub');
48
+ }
49
+ }
50
+
51
+ return count;
52
+ }
package/src/status.mjs ADDED
@@ -0,0 +1,43 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { c, log, detectWorkspace } from './utils.mjs';
4
+
5
+ export function runStatus() {
6
+ const workspace = detectWorkspace();
7
+ if (!workspace) {
8
+ log.err('No workspace detected. Set CLAWKIT_WORKSPACE or run from ~/.openclaw/workspace');
9
+ process.exit(1);
10
+ }
11
+
12
+ const configPath = join(workspace, 'skills', '.clawkit', 'config.json');
13
+ let config = {};
14
+ if (existsSync(configPath)) {
15
+ try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch { /* ignore */ }
16
+ }
17
+
18
+ const hasAgents = existsSync(join(workspace, 'AGENTS.md'));
19
+ const hasMemory = existsSync(join(workspace, 'MEMORY.md'));
20
+ const hasCron = existsSync(join(workspace, 'scripts/nightly-consolidation.sh'));
21
+
22
+ let skillCount = 0;
23
+ const skillsDir = join(workspace, 'skills');
24
+ if (existsSync(skillsDir)) {
25
+ skillCount = readdirSync(skillsDir, { withFileTypes: true })
26
+ .filter(d => d.isDirectory() && d.name !== '.clawkit' && d.name !== '.clawhub')
27
+ .length;
28
+ }
29
+
30
+ const check = (ok) => ok ? `${c.green}✔${c.reset}` : `${c.red}✖${c.reset}`;
31
+
32
+ console.log(`\n${c.bold}${c.cyan}🧰 ClawKit Status${c.reset}\n`);
33
+ console.log(` Workspace: ${workspace}`);
34
+ console.log(` Agent: ${config.agentName || 'Unknown'}`);
35
+ console.log(` Tier: ${config.tier || 'free'}`);
36
+ console.log(` Version: ${config.version || 'unknown'}`);
37
+ console.log(` Installed: ${config.installedAt || 'unknown'}\n`);
38
+ console.log(` ${check(hasAgents)} AGENTS.md`);
39
+ console.log(` ${check(hasMemory)} Memory system`);
40
+ console.log(` ${check(hasCron)} Nightly consolidation`);
41
+ console.log(` ${check(skillCount > 0)} Skills (${skillCount} installed)`);
42
+ console.log();
43
+ }
package/src/update.mjs ADDED
@@ -0,0 +1,100 @@
1
+ import { readFileSync, existsSync, readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { createHash } from 'crypto';
4
+ import https from 'https';
5
+ import { c, log, detectWorkspace } from './utils.mjs';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ function httpGet(url) {
9
+ return new Promise((resolve, reject) => {
10
+ https.get(url, (res) => {
11
+ let body = '';
12
+ res.on('data', d => body += d);
13
+ res.on('end', () => resolve(body));
14
+ }).on('error', reject).setTimeout(8000, function() { this.destroy(); reject(new Error('timeout')); });
15
+ });
16
+ }
17
+
18
+ function fileHash(path) {
19
+ try {
20
+ return createHash('md5').update(readFileSync(path)).digest('hex');
21
+ } catch { return null; }
22
+ }
23
+
24
+ export async function runUpdate(flags) {
25
+ const pkgDir = join(fileURLToPath(new URL('.', import.meta.url)), '..');
26
+ let localPkg;
27
+ try { localPkg = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf8')); } catch {
28
+ log.err('Cannot read package.json');
29
+ process.exit(1);
30
+ }
31
+ const currentVersion = localPkg.version;
32
+
33
+ console.log(`\n ${c.cyan}⚡ ClawKit Update Check${c.reset}\n`);
34
+ console.log(` Current: v${currentVersion}`);
35
+
36
+ // Check latest from npm
37
+ let latestVersion = null;
38
+ try {
39
+ const data = JSON.parse(await httpGet('https://registry.npmjs.org/clawkit/latest'));
40
+ latestVersion = data.version;
41
+ } catch {
42
+ log.warn('Could not check npm registry (offline?)');
43
+ }
44
+
45
+ if (latestVersion) {
46
+ console.log(` Latest: v${latestVersion}`);
47
+ if (latestVersion !== currentVersion) {
48
+ console.log(`\n ${c.yellow}⚠${c.reset} Update available! Run:`);
49
+ console.log(` ${c.cyan}npm update -g clawkit${c.reset}\n`);
50
+ } else {
51
+ console.log(` ${c.green}✔${c.reset} You're on the latest version!\n`);
52
+ }
53
+ }
54
+
55
+ // Check workspace template files
56
+ const ws = detectWorkspace();
57
+ if (!ws) {
58
+ log.warn('No workspace detected. Skipping template check.');
59
+ return;
60
+ }
61
+
62
+ console.log(` ${c.dim}Checking workspace templates...${c.reset}`);
63
+
64
+ // Template files that ship with clawkit init
65
+ const templateDir = join(pkgDir, 'templates');
66
+ const templateFiles = [
67
+ { template: 'SOUL.md', dest: 'SOUL.md' },
68
+ { template: 'AGENTS.md', dest: 'AGENTS.md' },
69
+ { template: 'LEARNINGS.md', dest: 'LEARNINGS.md' },
70
+ ];
71
+
72
+ for (const { template, dest } of templateFiles) {
73
+ const templatePath = join(templateDir, template);
74
+ const destPath = join(ws, dest);
75
+
76
+ if (!existsSync(templatePath)) continue;
77
+ if (!existsSync(destPath)) {
78
+ log.info(`${dest} — missing, would scaffold`);
79
+ continue;
80
+ }
81
+
82
+ const origHash = fileHash(templatePath);
83
+ const currentHash = fileHash(destPath);
84
+
85
+ if (origHash === currentHash) {
86
+ log.ok(`${dest} — unchanged, can update`);
87
+ } else {
88
+ console.log(` ${c.yellow}⏭${c.reset} Skipping ${dest} (customized)`);
89
+ }
90
+ }
91
+
92
+ // Check skills
93
+ const skillsDir = join(ws, 'skills');
94
+ if (existsSync(skillsDir)) {
95
+ const skills = readdirSync(skillsDir).filter(f => !f.startsWith('.'));
96
+ console.log(`\n ${c.dim}Skills installed: ${skills.length}${c.reset}`);
97
+ }
98
+
99
+ console.log('');
100
+ }
@@ -0,0 +1,104 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { execSync } from 'child_process';
4
+ import { c, log, detectWorkspace } from './utils.mjs';
5
+
6
+ export async function runUpgrade(flags) {
7
+ const ws = detectWorkspace();
8
+ const configDir = ws ? join(ws, 'skills', '.clawkit') : null;
9
+ const configPath = configDir ? join(configDir, 'config.json') : null;
10
+
11
+ let config = {};
12
+ if (configPath && existsSync(configPath)) {
13
+ try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
14
+ }
15
+
16
+ const currentKey = process.env.CLAWKIT_API_KEY || config.apiKey || '';
17
+ const isPro = currentKey.includes('_pro_');
18
+
19
+ // If --key provided, activate pro
20
+ if (flags.key) {
21
+ const key = flags.key;
22
+ if (!key.startsWith('clk_pro_')) {
23
+ log.err('Invalid Pro key. Keys start with clk_pro_');
24
+ process.exit(1);
25
+ }
26
+
27
+ // Validate key (basic format check + API call)
28
+ log.info('Validating Pro key...');
29
+ let valid = false;
30
+ try {
31
+ // Try API validation
32
+ const https = await import('https');
33
+ valid = await new Promise((resolve) => {
34
+ const req = https.request('https://clawkit-api.vercel.app/api/validate-key?key=' + encodeURIComponent(key), (res) => {
35
+ let body = '';
36
+ res.on('data', d => body += d);
37
+ res.on('end', () => {
38
+ try { resolve(JSON.parse(body).valid === true); } catch { resolve(false); }
39
+ });
40
+ });
41
+ req.on('error', () => resolve(false));
42
+ req.setTimeout(5000, () => { req.destroy(); resolve(false); });
43
+ req.end();
44
+ });
45
+ } catch {
46
+ // Offline — accept format-valid keys
47
+ valid = key.startsWith('clk_pro_');
48
+ }
49
+
50
+ if (!valid) {
51
+ log.err('Key validation failed. Check your key and try again.');
52
+ process.exit(1);
53
+ }
54
+
55
+ // Update config
56
+ if (configDir) {
57
+ mkdirSync(configDir, { recursive: true });
58
+ config.apiKey = key;
59
+ config.tier = 'pro';
60
+ config.upgradedAt = new Date().toISOString();
61
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
62
+ }
63
+
64
+ console.log(`\n ${c.green}🎉 Upgraded to Pro!${c.reset}\n`);
65
+ console.log(` ${c.cyan}Key:${c.reset} ${key.slice(0, 12)}...`);
66
+ console.log(` ${c.cyan}Features unlocked:${c.reset} Dashboard chat, file editing, premium skills\n`);
67
+ return;
68
+ }
69
+
70
+ // Already pro?
71
+ if (isPro) {
72
+ console.log(`\n ${c.green}✔${c.reset} You're already on ${c.yellow}Pro${c.reset}!\n`);
73
+ console.log(` Key: ${currentKey.slice(0, 12)}...`);
74
+ return;
75
+ }
76
+
77
+ // Show upgrade pitch
78
+ console.log(`
79
+ ${c.yellow}⚡ Upgrade to ClawKit Pro${c.reset}
80
+
81
+ ${c.green}What you get:${c.reset}
82
+ ✔ Full dashboard with live chat
83
+ ✔ Task management & file editing
84
+ ✔ Premium skill templates
85
+ ✔ Priority support
86
+ ✔ Advanced memory management
87
+
88
+ ${c.cyan}Pricing:${c.reset} $10/month
89
+
90
+ ${c.dim}Get your Pro key at:${c.reset}
91
+ ${c.cyan}https://clawkit.net/get-started${c.reset}
92
+
93
+ Then run: ${c.bold}clawkit upgrade --key clk_pro_...${c.reset}
94
+ `);
95
+
96
+ // Open browser
97
+ try {
98
+ const url = 'https://clawkit.net/get-started';
99
+ const platform = process.platform;
100
+ if (platform === 'darwin') execSync(`open ${url}`);
101
+ else if (platform === 'win32') execSync(`start ${url}`);
102
+ else execSync(`xdg-open ${url} 2>/dev/null || true`);
103
+ } catch {}
104
+ }