@1tool/js-boost 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/README.md +200 -0
- package/bin/js-boost.js +2 -0
- package/package.json +35 -0
- package/src/agents.js +62 -0
- package/src/cli.js +162 -0
- package/src/configure/mcp.js +223 -0
- package/src/generators/agents.js +64 -0
- package/src/generators/claude.js +60 -0
- package/src/generators/cursor.js +69 -0
- package/src/generators/junie.js +42 -0
- package/src/generators/kiro.js +35 -0
- package/src/index.js +125 -0
- package/src/init.js +134 -0
- package/src/utils/detect.js +58 -0
- package/src/utils/mcp.js +97 -0
- package/src/utils/reader.js +92 -0
- package/src/watch.js +58 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Read all markdown files from .ai/guidelines/
|
|
7
|
+
* Returns array of { filename, title, content }
|
|
8
|
+
*/
|
|
9
|
+
export async function readGuidelines(aiDir) {
|
|
10
|
+
const guidelinesDir = path.join(aiDir, 'guidelines');
|
|
11
|
+
if (!fs.existsSync(guidelinesDir)) return [];
|
|
12
|
+
|
|
13
|
+
const files = await glob('**/*.md', { cwd: guidelinesDir, absolute: false });
|
|
14
|
+
files.sort();
|
|
15
|
+
|
|
16
|
+
return files.map(file => {
|
|
17
|
+
const fullPath = path.join(guidelinesDir, file);
|
|
18
|
+
const content = fs.readFileSync(fullPath, 'utf8').trim();
|
|
19
|
+
const firstLine = content.split('\n')[0];
|
|
20
|
+
const title = firstLine.startsWith('#')
|
|
21
|
+
? firstLine.replace(/^#+\s*/, '').trim()
|
|
22
|
+
: path.basename(file, '.md');
|
|
23
|
+
return { filename: file, title, content };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read all skills from .ai/skills/
|
|
29
|
+
* Each skill is a folder containing SKILL.md with YAML frontmatter
|
|
30
|
+
* Returns array of { name, description, dir, content }
|
|
31
|
+
*/
|
|
32
|
+
export async function readSkills(aiDir) {
|
|
33
|
+
const skillsDir = path.join(aiDir, 'skills');
|
|
34
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
35
|
+
|
|
36
|
+
const skillFiles = await glob('*/SKILL.md', { cwd: skillsDir, absolute: false });
|
|
37
|
+
skillFiles.sort();
|
|
38
|
+
|
|
39
|
+
return skillFiles.map(file => {
|
|
40
|
+
const fullPath = path.join(skillsDir, file);
|
|
41
|
+
const content = fs.readFileSync(fullPath, 'utf8').trim();
|
|
42
|
+
const dir = path.dirname(file);
|
|
43
|
+
|
|
44
|
+
// Parse YAML frontmatter: ---\nname: ...\ndescription: ...\n---
|
|
45
|
+
let name = dir;
|
|
46
|
+
let description = '';
|
|
47
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
48
|
+
if (fmMatch) {
|
|
49
|
+
const fm = fmMatch[1];
|
|
50
|
+
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
51
|
+
const descMatch = fm.match(/^description:\s*(.+)$/m);
|
|
52
|
+
if (nameMatch) name = nameMatch[1].trim();
|
|
53
|
+
if (descMatch) description = descMatch[1].trim();
|
|
54
|
+
} else {
|
|
55
|
+
// Fallback: try first heading
|
|
56
|
+
const headingMatch = content.match(/^#+\s*(.+)$/m);
|
|
57
|
+
if (headingMatch) name = headingMatch[1].trim();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { name, description, dir, skillFile: file, content };
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Read js-boost.config.json if it exists
|
|
66
|
+
*/
|
|
67
|
+
export function readConfig(projectDir) {
|
|
68
|
+
const configPath = path.join(projectDir, 'js-boost.config.json');
|
|
69
|
+
if (!fs.existsSync(configPath)) return {};
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
72
|
+
} catch {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Ensure a directory exists
|
|
79
|
+
*/
|
|
80
|
+
export function ensureDir(dirPath) {
|
|
81
|
+
if (!fs.existsSync(dirPath)) {
|
|
82
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Write a file, creating parent dirs as needed
|
|
88
|
+
*/
|
|
89
|
+
export function writeFile(filePath, content) {
|
|
90
|
+
ensureDir(path.dirname(filePath));
|
|
91
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
92
|
+
}
|
package/src/watch.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import chokidar from 'chokidar';
|
|
4
|
+
import { generate } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function watch(projectDir) {
|
|
7
|
+
const aiDir = path.join(projectDir, '.ai');
|
|
8
|
+
const configPath = path.join(projectDir, 'js-boost.config.json');
|
|
9
|
+
|
|
10
|
+
console.log('');
|
|
11
|
+
console.log(chalk.bold.blue('⚡ js-boost') + chalk.dim(' — watch mode'));
|
|
12
|
+
console.log(chalk.dim(` Watching: ${aiDir}`));
|
|
13
|
+
console.log(chalk.dim(' Press Ctrl+C to stop'));
|
|
14
|
+
console.log('');
|
|
15
|
+
|
|
16
|
+
// Initial generation
|
|
17
|
+
generate(projectDir).catch(err => {
|
|
18
|
+
console.error(chalk.red(' Error during initial generation:'), err.message);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const watcher = chokidar.watch([
|
|
22
|
+
path.join(aiDir, '**', '*.md'),
|
|
23
|
+
configPath,
|
|
24
|
+
], {
|
|
25
|
+
ignoreInitial: true,
|
|
26
|
+
persistent: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let debounceTimer = null;
|
|
30
|
+
|
|
31
|
+
const handleChange = (filePath) => {
|
|
32
|
+
const rel = path.relative(projectDir, filePath);
|
|
33
|
+
console.log(chalk.dim(` Changed: ${rel}`));
|
|
34
|
+
|
|
35
|
+
// Debounce — wait 300ms after last change before regenerating
|
|
36
|
+
clearTimeout(debounceTimer);
|
|
37
|
+
debounceTimer = setTimeout(async () => {
|
|
38
|
+
try {
|
|
39
|
+
await generate(projectDir);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(chalk.red(' Error during regeneration:'), err.message);
|
|
42
|
+
}
|
|
43
|
+
}, 300);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
watcher
|
|
47
|
+
.on('add', handleChange)
|
|
48
|
+
.on('change', handleChange)
|
|
49
|
+
.on('unlink', handleChange)
|
|
50
|
+
.on('error', err => console.error(chalk.red(' Watcher error:'), err));
|
|
51
|
+
|
|
52
|
+
process.on('SIGINT', () => {
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(chalk.dim(' Stopping watcher...'));
|
|
55
|
+
watcher.close();
|
|
56
|
+
process.exit(0);
|
|
57
|
+
});
|
|
58
|
+
}
|