codingbuddy-rules 4.5.0 → 5.1.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/.ai-rules/adapters/aider.md +374 -0
- package/.ai-rules/adapters/antigravity.md +6 -6
- package/.ai-rules/adapters/claude-code.md +68 -4
- package/.ai-rules/adapters/codex.md +5 -5
- package/.ai-rules/adapters/cursor.md +2 -2
- package/.ai-rules/adapters/kiro.md +8 -8
- package/.ai-rules/adapters/opencode.md +7 -7
- package/.ai-rules/adapters/q.md +2 -2
- package/.ai-rules/adapters/windsurf.md +395 -0
- package/.ai-rules/agents/README.md +66 -16
- package/.ai-rules/agents/accessibility-specialist.json +8 -1
- package/.ai-rules/agents/act-mode.json +8 -1
- package/.ai-rules/agents/agent-architect.json +14 -7
- package/.ai-rules/agents/ai-ml-engineer.json +7 -0
- package/.ai-rules/agents/architecture-specialist.json +7 -0
- package/.ai-rules/agents/auto-mode.json +10 -2
- package/.ai-rules/agents/backend-developer.json +7 -0
- package/.ai-rules/agents/code-quality-specialist.json +7 -0
- package/.ai-rules/agents/code-reviewer.json +86 -64
- package/.ai-rules/agents/data-engineer.json +14 -7
- package/.ai-rules/agents/data-scientist.json +16 -9
- package/.ai-rules/agents/devops-engineer.json +7 -0
- package/.ai-rules/agents/documentation-specialist.json +7 -0
- package/.ai-rules/agents/eval-mode.json +30 -19
- package/.ai-rules/agents/event-architecture-specialist.json +7 -0
- package/.ai-rules/agents/frontend-developer.json +7 -0
- package/.ai-rules/agents/i18n-specialist.json +8 -1
- package/.ai-rules/agents/integration-specialist.json +7 -0
- package/.ai-rules/agents/migration-specialist.json +7 -0
- package/.ai-rules/agents/mobile-developer.json +8 -10
- package/.ai-rules/agents/observability-specialist.json +7 -0
- package/.ai-rules/agents/parallel-orchestrator.json +352 -0
- package/.ai-rules/agents/performance-specialist.json +7 -0
- package/.ai-rules/agents/plan-mode.json +9 -1
- package/.ai-rules/agents/plan-reviewer.json +211 -0
- package/.ai-rules/agents/platform-engineer.json +7 -0
- package/.ai-rules/agents/security-engineer.json +15 -8
- package/.ai-rules/agents/security-specialist.json +8 -1
- package/.ai-rules/agents/seo-specialist.json +7 -0
- package/.ai-rules/agents/software-engineer.json +7 -0
- package/.ai-rules/agents/solution-architect.json +17 -10
- package/.ai-rules/agents/systems-developer.json +15 -8
- package/.ai-rules/agents/technical-planner.json +17 -10
- package/.ai-rules/agents/test-engineer.json +13 -6
- package/.ai-rules/agents/test-strategy-specialist.json +7 -0
- package/.ai-rules/agents/tooling-engineer.json +10 -3
- package/.ai-rules/agents/ui-ux-designer.json +7 -0
- package/.ai-rules/keyword-modes.json +4 -4
- package/.ai-rules/rules/clarification-guide.md +14 -14
- package/.ai-rules/rules/core.md +73 -0
- package/.ai-rules/rules/parallel-execution.md +217 -0
- package/.ai-rules/schemas/agent.schema.json +38 -0
- package/.ai-rules/skills/README.md +29 -1
- package/.ai-rules/skills/agent-design/SKILL.md +5 -0
- package/.ai-rules/skills/agent-design/examples/agent-template.json +55 -0
- package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
- package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
- package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
- package/.ai-rules/skills/api-design/SKILL.md +5 -0
- package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
- package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
- package/.ai-rules/skills/build-fix/SKILL.md +234 -0
- package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
- package/.ai-rules/skills/context-management/SKILL.md +1 -0
- package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
- package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
- package/.ai-rules/skills/database-migration/SKILL.md +1 -0
- package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
- package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
- package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
- package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
- package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
- package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
- package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
- package/.ai-rules/skills/git-master/SKILL.md +358 -0
- package/.ai-rules/skills/incident-response/SKILL.md +1 -0
- package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
- package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
- package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
- package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +198 -0
- package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
- package/.ai-rules/skills/onboard/SKILL.md +150 -0
- package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
- package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
- package/.ai-rules/skills/plan-to-issues/SKILL.md +318 -0
- package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
- package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
- package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
- package/.ai-rules/skills/pr-review/SKILL.md +4 -0
- package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
- package/.ai-rules/skills/refactoring/SKILL.md +1 -0
- package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
- package/.ai-rules/skills/retrospective/SKILL.md +192 -0
- package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
- package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
- package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
- package/.ai-rules/skills/security-audit/SKILL.md +4 -0
- package/.ai-rules/skills/ship/SKILL.md +242 -0
- package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
- package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
- package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
- package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
- package/.ai-rules/skills/skill-creator/assets/eval_review.html +568 -0
- package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
- package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
- package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
- package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
- package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
- package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
- package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
- package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
- package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
- package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
- package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
- package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
- package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
- package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
- package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
- package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
- package/bin/cli.js +170 -0
- package/lib/init/detect-stack.js +162 -0
- package/lib/init/generate-config.js +31 -0
- package/lib/init/index.js +86 -0
- package/lib/init/prompt.js +60 -0
- package/lib/init/scaffold.js +67 -0
- package/lib/init/suggest-agent.js +57 -0
- package/package.json +10 -2
package/bin/cli.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const RULES_DIR = path.resolve(__dirname, '..', '.ai-rules');
|
|
9
|
+
|
|
10
|
+
function showHelp() {
|
|
11
|
+
console.log(
|
|
12
|
+
`
|
|
13
|
+
codingbuddy - AI coding rules CLI
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
codingbuddy <command> [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
init Initialize .ai-rules in the current project
|
|
20
|
+
validate Validate .ai-rules structure (agents JSON, rules markdown)
|
|
21
|
+
list-agents List available specialist agents
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--help, -h Show this help message
|
|
25
|
+
--version, -v Show version
|
|
26
|
+
`.trim(),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function showVersion() {
|
|
31
|
+
const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'));
|
|
32
|
+
console.log(pkg.version);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function listAgents() {
|
|
36
|
+
const agentsDir = path.join(RULES_DIR, 'agents');
|
|
37
|
+
if (!fs.existsSync(agentsDir)) {
|
|
38
|
+
console.error('Error: agents directory not found at', agentsDir);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const files = fs
|
|
43
|
+
.readdirSync(agentsDir)
|
|
44
|
+
.filter(f => f.endsWith('.json'))
|
|
45
|
+
.sort();
|
|
46
|
+
|
|
47
|
+
console.log(`Available agents (${files.length}):\n`);
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
try {
|
|
50
|
+
const agent = JSON.parse(fs.readFileSync(path.join(agentsDir, file), 'utf8'));
|
|
51
|
+
const name = agent.name || path.basename(file, '.json');
|
|
52
|
+
const desc = agent.description || '';
|
|
53
|
+
console.log(` ${name.padEnd(30)} ${desc}`);
|
|
54
|
+
} catch {
|
|
55
|
+
console.log(` ${path.basename(file, '.json').padEnd(30)} (parse error)`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function validate() {
|
|
61
|
+
let errors = 0;
|
|
62
|
+
|
|
63
|
+
// Check .ai-rules directory exists
|
|
64
|
+
if (!fs.existsSync(RULES_DIR)) {
|
|
65
|
+
console.error('Error: .ai-rules directory not found at', RULES_DIR);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate agents JSON
|
|
70
|
+
const agentsDir = path.join(RULES_DIR, 'agents');
|
|
71
|
+
if (fs.existsSync(agentsDir)) {
|
|
72
|
+
const jsonFiles = fs.readdirSync(agentsDir).filter(f => f.endsWith('.json'));
|
|
73
|
+
for (const file of jsonFiles) {
|
|
74
|
+
try {
|
|
75
|
+
JSON.parse(fs.readFileSync(path.join(agentsDir, file), 'utf8'));
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error(`FAIL agents/${file}: invalid JSON - ${e.message}`);
|
|
78
|
+
errors++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log(`OK agents/ - ${jsonFiles.length} JSON files valid`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Validate rules markdown files exist
|
|
85
|
+
const rulesDir = path.join(RULES_DIR, 'rules');
|
|
86
|
+
if (fs.existsSync(rulesDir)) {
|
|
87
|
+
const mdFiles = fs.readdirSync(rulesDir).filter(f => f.endsWith('.md'));
|
|
88
|
+
if (mdFiles.length === 0) {
|
|
89
|
+
console.error('FAIL rules/ - no markdown files found');
|
|
90
|
+
errors++;
|
|
91
|
+
} else {
|
|
92
|
+
console.log(`OK rules/ - ${mdFiles.length} markdown files found`);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
console.error('FAIL rules/ directory missing');
|
|
96
|
+
errors++;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Validate schemas directory
|
|
100
|
+
const schemasDir = path.join(RULES_DIR, 'schemas');
|
|
101
|
+
if (fs.existsSync(schemasDir)) {
|
|
102
|
+
const schemaFiles = fs.readdirSync(schemasDir).filter(f => f.endsWith('.json'));
|
|
103
|
+
for (const file of schemaFiles) {
|
|
104
|
+
try {
|
|
105
|
+
JSON.parse(fs.readFileSync(path.join(schemasDir, file), 'utf8'));
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.error(`FAIL schemas/${file}: invalid JSON - ${e.message}`);
|
|
108
|
+
errors++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
console.log(`OK schemas/ - ${schemaFiles.length} JSON files valid`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Validate keyword-modes.json
|
|
115
|
+
const keywordModes = path.join(RULES_DIR, 'keyword-modes.json');
|
|
116
|
+
if (fs.existsSync(keywordModes)) {
|
|
117
|
+
try {
|
|
118
|
+
JSON.parse(fs.readFileSync(keywordModes, 'utf8'));
|
|
119
|
+
console.log('OK keyword-modes.json valid');
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error(`FAIL keyword-modes.json: invalid JSON - ${e.message}`);
|
|
122
|
+
errors++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (errors > 0) {
|
|
127
|
+
console.error(`\nValidation failed with ${errors} error(s)`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
console.log('\nAll validations passed');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function init() {
|
|
134
|
+
const { run } = require('../lib/init');
|
|
135
|
+
run().catch(err => {
|
|
136
|
+
console.error('Error:', err.message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- Main ---
|
|
142
|
+
|
|
143
|
+
const args = process.argv.slice(2);
|
|
144
|
+
const command = args[0];
|
|
145
|
+
|
|
146
|
+
if (!command || command === '--help' || command === '-h') {
|
|
147
|
+
showHelp();
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (command === '--version' || command === '-v') {
|
|
152
|
+
showVersion();
|
|
153
|
+
process.exit(0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
switch (command) {
|
|
157
|
+
case 'init':
|
|
158
|
+
init();
|
|
159
|
+
break;
|
|
160
|
+
case 'validate':
|
|
161
|
+
validate();
|
|
162
|
+
break;
|
|
163
|
+
case 'list-agents':
|
|
164
|
+
listAgents();
|
|
165
|
+
break;
|
|
166
|
+
default:
|
|
167
|
+
console.error(`Unknown command: ${command}\n`);
|
|
168
|
+
showHelp();
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const FRONTEND_FRAMEWORKS = ['react', 'vue', 'angular', 'svelte', 'solid-js'];
|
|
7
|
+
const FULLSTACK_FRAMEWORKS = ['next', 'nuxt', 'remix', 'sveltekit', 'astro'];
|
|
8
|
+
const BACKEND_FRAMEWORKS = [
|
|
9
|
+
'@nestjs/core',
|
|
10
|
+
'express',
|
|
11
|
+
'fastify',
|
|
12
|
+
'koa',
|
|
13
|
+
'hapi',
|
|
14
|
+
'@hono/node-server',
|
|
15
|
+
'hono',
|
|
16
|
+
];
|
|
17
|
+
const MOBILE_FRAMEWORKS = ['react-native', 'expo', '@capacitor/core', 'ionic'];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detect tech stack from project files in the given directory.
|
|
21
|
+
* @param {string} cwd - Directory to scan
|
|
22
|
+
* @returns {{ runtime: string, language: string, frameworks: string[], category: string }}
|
|
23
|
+
*/
|
|
24
|
+
function detectStack(cwd) {
|
|
25
|
+
const result = {
|
|
26
|
+
runtime: 'unknown',
|
|
27
|
+
language: 'javascript',
|
|
28
|
+
frameworks: [],
|
|
29
|
+
category: 'unknown',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (tryDetectNode(cwd, result)) return result;
|
|
33
|
+
if (tryDetectPython(cwd, result)) return result;
|
|
34
|
+
if (tryDetectGo(cwd, result)) return result;
|
|
35
|
+
if (tryDetectRust(cwd, result)) return result;
|
|
36
|
+
|
|
37
|
+
result.language = 'unknown';
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function tryDetectNode(cwd, result) {
|
|
42
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
43
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
44
|
+
|
|
45
|
+
let pkg;
|
|
46
|
+
try {
|
|
47
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
result.runtime = 'node';
|
|
53
|
+
|
|
54
|
+
const allDeps = {
|
|
55
|
+
...pkg.dependencies,
|
|
56
|
+
...pkg.devDependencies,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Detect language
|
|
60
|
+
if (allDeps.typescript) {
|
|
61
|
+
result.language = 'typescript';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Detect frameworks
|
|
65
|
+
const detected = [];
|
|
66
|
+
|
|
67
|
+
for (const fw of FULLSTACK_FRAMEWORKS) {
|
|
68
|
+
if (allDeps[fw]) detected.push(fw);
|
|
69
|
+
}
|
|
70
|
+
for (const fw of FRONTEND_FRAMEWORKS) {
|
|
71
|
+
if (allDeps[fw]) detected.push(fw);
|
|
72
|
+
}
|
|
73
|
+
for (const fw of BACKEND_FRAMEWORKS) {
|
|
74
|
+
if (allDeps[fw]) {
|
|
75
|
+
const name = fw.startsWith('@nestjs') ? 'nestjs' : fw;
|
|
76
|
+
detected.push(name);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const fw of MOBILE_FRAMEWORKS) {
|
|
80
|
+
if (allDeps[fw]) detected.push(fw);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
result.frameworks = detected;
|
|
84
|
+
|
|
85
|
+
// Determine category
|
|
86
|
+
if (detected.some(f => MOBILE_FRAMEWORKS.includes(f) || f === 'react-native' || f === 'expo')) {
|
|
87
|
+
result.category = 'mobile';
|
|
88
|
+
} else if (detected.some(f => FULLSTACK_FRAMEWORKS.includes(f))) {
|
|
89
|
+
result.category = 'fullstack';
|
|
90
|
+
} else if (detected.some(f => FRONTEND_FRAMEWORKS.includes(f))) {
|
|
91
|
+
result.category = 'frontend';
|
|
92
|
+
} else if (
|
|
93
|
+
detected.some(f =>
|
|
94
|
+
['nestjs', ...BACKEND_FRAMEWORKS.map(b => (b.startsWith('@') ? 'nestjs' : b))].includes(f),
|
|
95
|
+
)
|
|
96
|
+
) {
|
|
97
|
+
result.category = 'backend';
|
|
98
|
+
} else {
|
|
99
|
+
result.category = 'backend'; // default for Node without frameworks
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function tryDetectPython(cwd, result) {
|
|
106
|
+
const pyprojectPath = path.join(cwd, 'pyproject.toml');
|
|
107
|
+
if (!fs.existsSync(pyprojectPath)) return false;
|
|
108
|
+
|
|
109
|
+
result.runtime = 'python';
|
|
110
|
+
result.language = 'python';
|
|
111
|
+
result.category = 'backend';
|
|
112
|
+
|
|
113
|
+
const content = fs.readFileSync(pyprojectPath, 'utf-8');
|
|
114
|
+
const pyFrameworks = {
|
|
115
|
+
django: 'django',
|
|
116
|
+
flask: 'flask',
|
|
117
|
+
fastapi: 'fastapi',
|
|
118
|
+
starlette: 'starlette',
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
for (const [key, name] of Object.entries(pyFrameworks)) {
|
|
122
|
+
if (content.toLowerCase().includes(key)) {
|
|
123
|
+
result.frameworks.push(name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function tryDetectGo(cwd, result) {
|
|
131
|
+
const goModPath = path.join(cwd, 'go.mod');
|
|
132
|
+
if (!fs.existsSync(goModPath)) return false;
|
|
133
|
+
|
|
134
|
+
result.runtime = 'go';
|
|
135
|
+
result.language = 'go';
|
|
136
|
+
result.category = 'backend';
|
|
137
|
+
|
|
138
|
+
const content = fs.readFileSync(goModPath, 'utf-8');
|
|
139
|
+
if (content.includes('github.com/gin-gonic/gin')) result.frameworks.push('gin');
|
|
140
|
+
if (content.includes('github.com/gofiber/fiber')) result.frameworks.push('fiber');
|
|
141
|
+
if (content.includes('github.com/labstack/echo')) result.frameworks.push('echo');
|
|
142
|
+
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function tryDetectRust(cwd, result) {
|
|
147
|
+
const cargoPath = path.join(cwd, 'Cargo.toml');
|
|
148
|
+
if (!fs.existsSync(cargoPath)) return false;
|
|
149
|
+
|
|
150
|
+
result.runtime = 'rust';
|
|
151
|
+
result.language = 'rust';
|
|
152
|
+
result.category = 'backend';
|
|
153
|
+
|
|
154
|
+
const content = fs.readFileSync(cargoPath, 'utf-8');
|
|
155
|
+
if (content.includes('actix-web')) result.frameworks.push('actix-web');
|
|
156
|
+
if (content.includes('axum')) result.frameworks.push('axum');
|
|
157
|
+
if (content.includes('rocket')) result.frameworks.push('rocket');
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = { detectStack };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate codingbuddy.config.json in the target directory.
|
|
8
|
+
* @param {string} cwd - Target directory
|
|
9
|
+
* @param {{ language: string, primaryAgent: string, techStack: object }} options
|
|
10
|
+
* @returns {{ created: boolean, skipped: boolean, path: string }}
|
|
11
|
+
*/
|
|
12
|
+
function generateConfig(cwd, options) {
|
|
13
|
+
const configPath = path.join(cwd, 'codingbuddy.config.json');
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(configPath)) {
|
|
16
|
+
return { created: false, skipped: true, path: configPath };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const config = {
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
language: options.language || 'en',
|
|
22
|
+
primaryAgent: options.primaryAgent || 'software-engineer',
|
|
23
|
+
techStack: options.techStack || {},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
27
|
+
|
|
28
|
+
return { created: true, skipped: false, path: configPath };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { generateConfig };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { detectStack } = require('./detect-stack');
|
|
5
|
+
const { suggestAgent } = require('./suggest-agent');
|
|
6
|
+
const { generateConfig } = require('./generate-config');
|
|
7
|
+
const { scaffold } = require('./scaffold');
|
|
8
|
+
const { ask, select, confirm } = require('./prompt');
|
|
9
|
+
|
|
10
|
+
const LANGUAGES = ['en', 'ko', 'ja', 'zh', 'es'];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Run the codingbuddy init wizard.
|
|
14
|
+
* @param {string} [cwd=process.cwd()]
|
|
15
|
+
*/
|
|
16
|
+
async function run(cwd) {
|
|
17
|
+
const targetDir = cwd || process.cwd();
|
|
18
|
+
|
|
19
|
+
console.log('\n codingbuddy init\n');
|
|
20
|
+
console.log(' Initializing codingbuddy for your project...\n');
|
|
21
|
+
|
|
22
|
+
// Step 1: Detect tech stack
|
|
23
|
+
console.log(' Detecting tech stack...');
|
|
24
|
+
const stack = detectStack(targetDir);
|
|
25
|
+
|
|
26
|
+
if (stack.runtime !== 'unknown') {
|
|
27
|
+
console.log(` Detected: ${stack.runtime} (${stack.language})`);
|
|
28
|
+
if (stack.frameworks.length > 0) {
|
|
29
|
+
console.log(` Frameworks: ${stack.frameworks.join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
console.log(` Category: ${stack.category}`);
|
|
32
|
+
} else {
|
|
33
|
+
console.log(' No recognized project files found.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Step 2: Language selection
|
|
37
|
+
const language = await select('Select communication language:', LANGUAGES, 0);
|
|
38
|
+
console.log(` Language: ${language}`);
|
|
39
|
+
|
|
40
|
+
// Step 3: Agent suggestion
|
|
41
|
+
const suggested = suggestAgent(stack);
|
|
42
|
+
console.log(`\n Recommended primary agent: ${suggested}`);
|
|
43
|
+
const useAgent = await confirm(` Use ${suggested} as primary agent?`, true);
|
|
44
|
+
|
|
45
|
+
let primaryAgent = suggested;
|
|
46
|
+
if (!useAgent) {
|
|
47
|
+
primaryAgent = await ask(' Enter agent name', 'software-engineer');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 4: Generate config
|
|
51
|
+
console.log('\n Creating codingbuddy.config.json...');
|
|
52
|
+
const configResult = generateConfig(targetDir, {
|
|
53
|
+
language,
|
|
54
|
+
primaryAgent,
|
|
55
|
+
techStack: stack,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (configResult.skipped) {
|
|
59
|
+
console.log(' codingbuddy.config.json already exists, skipped.');
|
|
60
|
+
} else {
|
|
61
|
+
console.log(' Created codingbuddy.config.json');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Step 5: Scaffold .ai-rules
|
|
65
|
+
const doScaffold = await confirm(' Scaffold .ai-rules/ directory with default rules?', true);
|
|
66
|
+
|
|
67
|
+
if (doScaffold) {
|
|
68
|
+
console.log(' Scaffolding .ai-rules/...');
|
|
69
|
+
const scaffoldResult = scaffold(targetDir);
|
|
70
|
+
|
|
71
|
+
if (scaffoldResult.skipped) {
|
|
72
|
+
console.log(' .ai-rules/ already exists, skipped.');
|
|
73
|
+
} else {
|
|
74
|
+
console.log(` Created .ai-rules/ with: ${scaffoldResult.dirs.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Done
|
|
79
|
+
console.log('\n Done! codingbuddy is ready.\n');
|
|
80
|
+
console.log(' Next steps:');
|
|
81
|
+
console.log(' 1. Review codingbuddy.config.json');
|
|
82
|
+
console.log(' 2. Customize .ai-rules/ for your project');
|
|
83
|
+
console.log(' 3. Start coding with your AI assistant\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { run };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const readline = require('node:readline');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ask a question via stdin and return the answer.
|
|
7
|
+
* @param {string} question
|
|
8
|
+
* @param {string} [defaultValue]
|
|
9
|
+
* @returns {Promise<string>}
|
|
10
|
+
*/
|
|
11
|
+
function ask(question, defaultValue) {
|
|
12
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
13
|
+
const suffix = defaultValue ? ` (${defaultValue})` : '';
|
|
14
|
+
|
|
15
|
+
return new Promise(resolve => {
|
|
16
|
+
rl.question(`${question}${suffix}: `, answer => {
|
|
17
|
+
rl.close();
|
|
18
|
+
resolve(answer.trim() || defaultValue || '');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Ask user to select from a list of options.
|
|
25
|
+
* @param {string} question
|
|
26
|
+
* @param {string[]} options
|
|
27
|
+
* @param {number} [defaultIndex=0]
|
|
28
|
+
* @returns {Promise<string>}
|
|
29
|
+
*/
|
|
30
|
+
async function select(question, options, defaultIndex = 0) {
|
|
31
|
+
console.log(`\n${question}`);
|
|
32
|
+
options.forEach((opt, i) => {
|
|
33
|
+
const marker = i === defaultIndex ? '>' : ' ';
|
|
34
|
+
console.log(` ${marker} ${i + 1}. ${opt}`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const answer = await ask('Select', String(defaultIndex + 1));
|
|
38
|
+
const index = parseInt(answer, 10) - 1;
|
|
39
|
+
|
|
40
|
+
if (index >= 0 && index < options.length) {
|
|
41
|
+
return options[index];
|
|
42
|
+
}
|
|
43
|
+
return options[defaultIndex];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Ask yes/no question.
|
|
48
|
+
* @param {string} question
|
|
49
|
+
* @param {boolean} [defaultValue=true]
|
|
50
|
+
* @returns {Promise<boolean>}
|
|
51
|
+
*/
|
|
52
|
+
async function confirm(question, defaultValue = true) {
|
|
53
|
+
const hint = defaultValue ? 'Y/n' : 'y/N';
|
|
54
|
+
const answer = await ask(`${question} (${hint})`, '');
|
|
55
|
+
|
|
56
|
+
if (!answer) return defaultValue;
|
|
57
|
+
return answer.toLowerCase().startsWith('y');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { ask, select, confirm };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_SOURCE = path.resolve(__dirname, '../../.ai-rules');
|
|
7
|
+
|
|
8
|
+
// Directories to always scaffold
|
|
9
|
+
const SCAFFOLD_DIRS = ['rules', 'agents'];
|
|
10
|
+
// Top-level files to always copy
|
|
11
|
+
const SCAFFOLD_FILES = ['README.md'];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Scaffold .ai-rules/ structure in the target directory.
|
|
15
|
+
* @param {string} cwd - Target directory
|
|
16
|
+
* @param {{ source?: string }} options
|
|
17
|
+
* @returns {{ skipped: boolean, dirs: string[], targetPath: string }}
|
|
18
|
+
*/
|
|
19
|
+
function scaffold(cwd, options = {}) {
|
|
20
|
+
const source = options.source || DEFAULT_SOURCE;
|
|
21
|
+
const targetDir = path.join(cwd, '.ai-rules');
|
|
22
|
+
|
|
23
|
+
if (fs.existsSync(targetDir)) {
|
|
24
|
+
return { skipped: true, dirs: [], targetPath: targetDir };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const copiedDirs = [];
|
|
30
|
+
|
|
31
|
+
// Copy directories
|
|
32
|
+
for (const dir of SCAFFOLD_DIRS) {
|
|
33
|
+
const srcDir = path.join(source, dir);
|
|
34
|
+
if (fs.existsSync(srcDir)) {
|
|
35
|
+
copyDirRecursive(srcDir, path.join(targetDir, dir));
|
|
36
|
+
copiedDirs.push(dir);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Copy top-level files
|
|
41
|
+
for (const file of SCAFFOLD_FILES) {
|
|
42
|
+
const srcFile = path.join(source, file);
|
|
43
|
+
if (fs.existsSync(srcFile)) {
|
|
44
|
+
fs.copyFileSync(srcFile, path.join(targetDir, file));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { skipped: false, dirs: copiedDirs, targetPath: targetDir };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function copyDirRecursive(src, dest) {
|
|
52
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
53
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
54
|
+
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const srcPath = path.join(src, entry.name);
|
|
57
|
+
const destPath = path.join(dest, entry.name);
|
|
58
|
+
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
copyDirRecursive(srcPath, destPath);
|
|
61
|
+
} else {
|
|
62
|
+
fs.copyFileSync(srcPath, destPath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { scaffold };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AGENT_MAP = {
|
|
4
|
+
mobile: 'mobile-developer',
|
|
5
|
+
frontend: 'frontend-developer',
|
|
6
|
+
fullstack: 'software-engineer',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const RUNTIME_AGENTS = {
|
|
10
|
+
rust: 'systems-developer',
|
|
11
|
+
go: 'backend-developer',
|
|
12
|
+
python: 'backend-developer',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const DATA_SCIENCE_FRAMEWORKS = [
|
|
16
|
+
'pandas',
|
|
17
|
+
'numpy',
|
|
18
|
+
'scipy',
|
|
19
|
+
'jupyter',
|
|
20
|
+
'tensorflow',
|
|
21
|
+
'pytorch',
|
|
22
|
+
'scikit-learn',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Suggest the best primary agent based on detected tech stack.
|
|
27
|
+
* @param {{ runtime: string, category: string, frameworks: string[] }} stack
|
|
28
|
+
* @returns {string} Agent name (e.g. 'frontend-developer')
|
|
29
|
+
*/
|
|
30
|
+
function suggestAgent(stack) {
|
|
31
|
+
// Data science detection for Python
|
|
32
|
+
if (
|
|
33
|
+
stack.runtime === 'python' &&
|
|
34
|
+
stack.frameworks.some(f => DATA_SCIENCE_FRAMEWORKS.includes(f))
|
|
35
|
+
) {
|
|
36
|
+
return 'data-scientist';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Category-based mapping
|
|
40
|
+
if (AGENT_MAP[stack.category]) {
|
|
41
|
+
return AGENT_MAP[stack.category];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Runtime-based mapping
|
|
45
|
+
if (RUNTIME_AGENTS[stack.runtime]) {
|
|
46
|
+
return RUNTIME_AGENTS[stack.runtime];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Backend Node.js
|
|
50
|
+
if (stack.runtime === 'node' && stack.category === 'backend') {
|
|
51
|
+
return 'backend-developer';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return 'software-engineer';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { suggestAgent };
|
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codingbuddy-rules",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "AI coding rules for consistent practices across AI assistants",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"codingbuddy": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test __tests__/**/*.test.js"
|
|
12
|
+
},
|
|
7
13
|
"files": [
|
|
8
14
|
"index.js",
|
|
9
15
|
"index.d.ts",
|
|
10
|
-
".ai-rules"
|
|
16
|
+
".ai-rules",
|
|
17
|
+
"bin",
|
|
18
|
+
"lib"
|
|
11
19
|
],
|
|
12
20
|
"keywords": [
|
|
13
21
|
"ai",
|