@shaykec/claude-teach 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -3
- package/src/cli.e2e.helpers.js +289 -0
- package/src/cli.e2e.test.js +962 -0
- package/src/cli.js +206 -2
package/src/cli.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { readFileSync, existsSync } from 'fs';
|
|
5
|
-
import { resolve, join } from 'path';
|
|
5
|
+
import { resolve, join, dirname } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
-
import {
|
|
7
|
+
import { exec, spawn, execFileSync as execFileSyncChild } from 'child_process';
|
|
8
|
+
import { createRequire } from 'module';
|
|
8
9
|
import chalk from 'chalk';
|
|
9
10
|
import { buildRegistry, loadRegistry, getModule } from './registry.js';
|
|
10
11
|
import { loadProgress, saveProgress, getStats } from './progress.js';
|
|
@@ -14,6 +15,73 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
14
15
|
const __dirname = dirname(__filename);
|
|
15
16
|
const ROOT = resolve(__dirname, '..', '..', '..');
|
|
16
17
|
|
|
18
|
+
// =====================================================================
|
|
19
|
+
// Helpers — plugin, extension, and Claude Code resolution
|
|
20
|
+
// =====================================================================
|
|
21
|
+
|
|
22
|
+
const require_ = createRequire(import.meta.url);
|
|
23
|
+
|
|
24
|
+
/** Resolve the Claude Code plugin directory. */
|
|
25
|
+
function resolvePluginDir() {
|
|
26
|
+
// Strategy 1: monorepo layout
|
|
27
|
+
const monoRepo = join(ROOT, 'packages', 'plugin');
|
|
28
|
+
if (existsSync(join(monoRepo, '.claude-plugin', 'plugin.json'))) return monoRepo;
|
|
29
|
+
// Strategy 2: resolve installed @shaykec/plugin package
|
|
30
|
+
try {
|
|
31
|
+
const pkgPath = require_.resolve('@shaykec/plugin/package.json');
|
|
32
|
+
const dir = dirname(pkgPath);
|
|
33
|
+
if (existsSync(join(dir, '.claude-plugin', 'plugin.json'))) return dir;
|
|
34
|
+
} catch { /* not installed */ }
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Resolve the Chrome extension directory. */
|
|
39
|
+
function resolveExtensionDir() {
|
|
40
|
+
const monoRepo = join(ROOT, 'packages', 'extension');
|
|
41
|
+
if (existsSync(join(monoRepo, 'manifest.json'))) return monoRepo;
|
|
42
|
+
try {
|
|
43
|
+
const pkgPath = require_.resolve('@shaykec/extension/package.json');
|
|
44
|
+
const dir = dirname(pkgPath);
|
|
45
|
+
if (existsSync(join(dir, 'manifest.json'))) return dir;
|
|
46
|
+
} catch { /* not installed */ }
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Check if the `claude` CLI is available on PATH. */
|
|
51
|
+
function findClaude() {
|
|
52
|
+
try {
|
|
53
|
+
const version = execFileSyncChild('claude', ['--version'], {
|
|
54
|
+
stdio: 'pipe', encoding: 'utf-8',
|
|
55
|
+
}).trim();
|
|
56
|
+
return { found: true, version };
|
|
57
|
+
} catch {
|
|
58
|
+
return { found: false, version: null };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Open the extension folder + chrome://extensions page (cross-platform). */
|
|
63
|
+
function openExtensionInstall(extensionDir) {
|
|
64
|
+
const platform = process.platform;
|
|
65
|
+
if (platform === 'darwin') {
|
|
66
|
+
exec(`open "${extensionDir}"`);
|
|
67
|
+
exec('open -a "Google Chrome" "chrome://extensions"');
|
|
68
|
+
} else if (platform === 'linux') {
|
|
69
|
+
exec(`xdg-open "${extensionDir}"`);
|
|
70
|
+
exec('google-chrome "chrome://extensions" 2>/dev/null || chromium-browser "chrome://extensions" 2>/dev/null');
|
|
71
|
+
} else if (platform === 'win32') {
|
|
72
|
+
exec(`explorer "${extensionDir}"`);
|
|
73
|
+
exec('start chrome "chrome://extensions"');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Open a URL in the default browser (cross-platform). */
|
|
78
|
+
function openUrl(url) {
|
|
79
|
+
const platform = process.platform;
|
|
80
|
+
if (platform === 'darwin') exec(`open "${url}"`);
|
|
81
|
+
else if (platform === 'linux') exec(`xdg-open "${url}"`);
|
|
82
|
+
else if (platform === 'win32') exec(`start "" "${url}"`);
|
|
83
|
+
}
|
|
84
|
+
|
|
17
85
|
const program = new Command();
|
|
18
86
|
|
|
19
87
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -36,6 +104,10 @@ Examples:
|
|
|
36
104
|
$ claude-teach search docker Search registries for packs
|
|
37
105
|
$ claude-teach author init my-pack Scaffold a new module pack
|
|
38
106
|
$ claude-teach author validate ./my-pack Validate a pack
|
|
107
|
+
$ claude-teach start Launch Claude Code with teaching skills
|
|
108
|
+
$ claude-teach start -p "teach me git" One-shot teaching prompt
|
|
109
|
+
$ claude-teach setup Check environment and plugin path
|
|
110
|
+
$ claude-teach setup --extension Install the Chrome extension
|
|
39
111
|
$ claude-teach serve Start the bridge server for visuals`)
|
|
40
112
|
.version(pkg.version);
|
|
41
113
|
|
|
@@ -218,6 +290,138 @@ program
|
|
|
218
290
|
showInbox();
|
|
219
291
|
});
|
|
220
292
|
|
|
293
|
+
// =====================================================================
|
|
294
|
+
// Plugin integration commands — launch Claude Code, setup environment
|
|
295
|
+
// =====================================================================
|
|
296
|
+
|
|
297
|
+
// --- start ---
|
|
298
|
+
program
|
|
299
|
+
.command('start')
|
|
300
|
+
.description('Launch Claude Code with ClaudeTeach — starts bridge server + loads teaching skills')
|
|
301
|
+
.option('--no-server', 'Skip starting the bridge server')
|
|
302
|
+
.option('--port <port>', 'Bridge server port', '3456')
|
|
303
|
+
.allowUnknownOption(true)
|
|
304
|
+
.action(async (opts, cmd) => {
|
|
305
|
+
// 1. Resolve plugin
|
|
306
|
+
const pluginDir = resolvePluginDir();
|
|
307
|
+
if (!pluginDir) {
|
|
308
|
+
console.error(chalk.red('Could not find the ClaudeTeach plugin directory.'));
|
|
309
|
+
console.error('Make sure @shaykec/plugin is installed or you are in the ClaudeTeach repo.');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 2. Check claude
|
|
314
|
+
const claude = findClaude();
|
|
315
|
+
if (!claude.found) {
|
|
316
|
+
console.error(chalk.red('Claude Code CLI not found.'));
|
|
317
|
+
console.error('Install it from: https://claude.ai/download');
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 3. Start bridge server (unless --no-server)
|
|
322
|
+
let server;
|
|
323
|
+
if (opts.server !== false) {
|
|
324
|
+
try {
|
|
325
|
+
const { startServer } = await import('@shaykec/bridge');
|
|
326
|
+
server = startServer({
|
|
327
|
+
port: parseInt(opts.port, 10),
|
|
328
|
+
progressProvider: { getProgress: () => loadProgress(ROOT) },
|
|
329
|
+
});
|
|
330
|
+
console.log(chalk.dim(` Bridge server on port ${opts.port}`));
|
|
331
|
+
} catch (err) {
|
|
332
|
+
console.warn(chalk.yellow(` Bridge server failed to start: ${err.message}`));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 4. Launch
|
|
337
|
+
console.log(chalk.green('Launching Claude Code with ClaudeTeach...'));
|
|
338
|
+
console.log(chalk.dim(` Plugin: ${pluginDir}`));
|
|
339
|
+
|
|
340
|
+
const claudeArgs = ['--plugin-dir', pluginDir, ...cmd.args];
|
|
341
|
+
const child = spawn('claude', claudeArgs, { stdio: 'inherit' });
|
|
342
|
+
|
|
343
|
+
child.on('error', (err) => {
|
|
344
|
+
console.error(chalk.red(`Failed to start Claude Code: ${err.message}`));
|
|
345
|
+
if (server) server.close();
|
|
346
|
+
process.exit(1);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
child.on('close', (code) => {
|
|
350
|
+
if (server) server.close();
|
|
351
|
+
process.exit(code ?? 0);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// --- setup ---
|
|
356
|
+
program
|
|
357
|
+
.command('setup')
|
|
358
|
+
.description('Check environment, show plugin status, and install Chrome extension')
|
|
359
|
+
.option('--show-path', 'Print only the plugin directory path')
|
|
360
|
+
.option('--extension', 'Open Chrome to install the ClaudeTeach extension')
|
|
361
|
+
.action(async (opts) => {
|
|
362
|
+
const pluginDir = resolvePluginDir();
|
|
363
|
+
const extensionDir = resolveExtensionDir();
|
|
364
|
+
|
|
365
|
+
// --show-path: machine-readable plugin path
|
|
366
|
+
if (opts.showPath) {
|
|
367
|
+
if (!pluginDir) process.exit(1);
|
|
368
|
+
console.log(pluginDir);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// --extension: open Finder + Chrome extensions page
|
|
373
|
+
if (opts.extension) {
|
|
374
|
+
if (!extensionDir) {
|
|
375
|
+
console.error(chalk.red('Could not find the ClaudeTeach extension directory.'));
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
openExtensionInstall(extensionDir);
|
|
379
|
+
console.log(chalk.green('\nChrome Extensions page + extension folder opened.'));
|
|
380
|
+
console.log(`\n 1. Enable ${chalk.cyan('Developer mode')} (toggle, top-right)`);
|
|
381
|
+
console.log(` 2. Click ${chalk.cyan('Load unpacked')}`);
|
|
382
|
+
console.log(` 3. Select the folder that just opened: ${chalk.cyan(extensionDir)}`);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Default: full setup — checklist + welcome page
|
|
387
|
+
const claude = findClaude();
|
|
388
|
+
|
|
389
|
+
console.log(chalk.bold('\n ClaudeTeach Setup\n'));
|
|
390
|
+
console.log(` Plugin path: ${pluginDir ? chalk.green(pluginDir) : chalk.red('not found')}`);
|
|
391
|
+
console.log(` Extension path: ${extensionDir ? chalk.green(extensionDir) : chalk.red('not found')}`);
|
|
392
|
+
console.log(` Claude Code: ${claude.found ? chalk.green(claude.version) : chalk.red('not installed')}`);
|
|
393
|
+
|
|
394
|
+
if (!pluginDir) {
|
|
395
|
+
console.error(chalk.red('\n Plugin not found. Make sure @shaykec/plugin is installed.'));
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!claude.found) {
|
|
400
|
+
console.log(chalk.yellow('\n Claude Code not found. Install from: https://claude.ai/download'));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Start bridge + open welcome page
|
|
404
|
+
let server;
|
|
405
|
+
try {
|
|
406
|
+
const { startServer } = await import('@shaykec/bridge');
|
|
407
|
+
const port = 3456;
|
|
408
|
+
server = startServer({
|
|
409
|
+
port,
|
|
410
|
+
progressProvider: { getProgress: () => loadProgress(ROOT) },
|
|
411
|
+
});
|
|
412
|
+
console.log(chalk.dim(`\n Bridge server started on port ${port}`));
|
|
413
|
+
openUrl(`http://localhost:${port}/?mode=welcome`);
|
|
414
|
+
console.log(chalk.green(' Welcome page opened in your browser.'));
|
|
415
|
+
} catch (err) {
|
|
416
|
+
console.warn(chalk.yellow(`\n Could not start bridge server: ${err.message}`));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
console.log(chalk.bold('\n Next steps:\n'));
|
|
420
|
+
console.log(` ${chalk.cyan('claude-teach start')} Launch Claude Code with teaching skills`);
|
|
421
|
+
console.log(` ${chalk.cyan('claude-teach setup --extension')} Install the Chrome extension`);
|
|
422
|
+
console.log();
|
|
423
|
+
});
|
|
424
|
+
|
|
221
425
|
// =====================================================================
|
|
222
426
|
// System commands
|
|
223
427
|
// =====================================================================
|