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.
Files changed (129) hide show
  1. package/.ai-rules/adapters/aider.md +374 -0
  2. package/.ai-rules/adapters/antigravity.md +6 -6
  3. package/.ai-rules/adapters/claude-code.md +68 -4
  4. package/.ai-rules/adapters/codex.md +5 -5
  5. package/.ai-rules/adapters/cursor.md +2 -2
  6. package/.ai-rules/adapters/kiro.md +8 -8
  7. package/.ai-rules/adapters/opencode.md +7 -7
  8. package/.ai-rules/adapters/q.md +2 -2
  9. package/.ai-rules/adapters/windsurf.md +395 -0
  10. package/.ai-rules/agents/README.md +66 -16
  11. package/.ai-rules/agents/accessibility-specialist.json +8 -1
  12. package/.ai-rules/agents/act-mode.json +8 -1
  13. package/.ai-rules/agents/agent-architect.json +14 -7
  14. package/.ai-rules/agents/ai-ml-engineer.json +7 -0
  15. package/.ai-rules/agents/architecture-specialist.json +7 -0
  16. package/.ai-rules/agents/auto-mode.json +10 -2
  17. package/.ai-rules/agents/backend-developer.json +7 -0
  18. package/.ai-rules/agents/code-quality-specialist.json +7 -0
  19. package/.ai-rules/agents/code-reviewer.json +86 -64
  20. package/.ai-rules/agents/data-engineer.json +14 -7
  21. package/.ai-rules/agents/data-scientist.json +16 -9
  22. package/.ai-rules/agents/devops-engineer.json +7 -0
  23. package/.ai-rules/agents/documentation-specialist.json +7 -0
  24. package/.ai-rules/agents/eval-mode.json +30 -19
  25. package/.ai-rules/agents/event-architecture-specialist.json +7 -0
  26. package/.ai-rules/agents/frontend-developer.json +7 -0
  27. package/.ai-rules/agents/i18n-specialist.json +8 -1
  28. package/.ai-rules/agents/integration-specialist.json +7 -0
  29. package/.ai-rules/agents/migration-specialist.json +7 -0
  30. package/.ai-rules/agents/mobile-developer.json +8 -10
  31. package/.ai-rules/agents/observability-specialist.json +7 -0
  32. package/.ai-rules/agents/parallel-orchestrator.json +352 -0
  33. package/.ai-rules/agents/performance-specialist.json +7 -0
  34. package/.ai-rules/agents/plan-mode.json +9 -1
  35. package/.ai-rules/agents/plan-reviewer.json +211 -0
  36. package/.ai-rules/agents/platform-engineer.json +7 -0
  37. package/.ai-rules/agents/security-engineer.json +15 -8
  38. package/.ai-rules/agents/security-specialist.json +8 -1
  39. package/.ai-rules/agents/seo-specialist.json +7 -0
  40. package/.ai-rules/agents/software-engineer.json +7 -0
  41. package/.ai-rules/agents/solution-architect.json +17 -10
  42. package/.ai-rules/agents/systems-developer.json +15 -8
  43. package/.ai-rules/agents/technical-planner.json +17 -10
  44. package/.ai-rules/agents/test-engineer.json +13 -6
  45. package/.ai-rules/agents/test-strategy-specialist.json +7 -0
  46. package/.ai-rules/agents/tooling-engineer.json +10 -3
  47. package/.ai-rules/agents/ui-ux-designer.json +7 -0
  48. package/.ai-rules/keyword-modes.json +4 -4
  49. package/.ai-rules/rules/clarification-guide.md +14 -14
  50. package/.ai-rules/rules/core.md +73 -0
  51. package/.ai-rules/rules/parallel-execution.md +217 -0
  52. package/.ai-rules/schemas/agent.schema.json +38 -0
  53. package/.ai-rules/skills/README.md +29 -1
  54. package/.ai-rules/skills/agent-design/SKILL.md +5 -0
  55. package/.ai-rules/skills/agent-design/examples/agent-template.json +55 -0
  56. package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
  57. package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
  58. package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
  59. package/.ai-rules/skills/api-design/SKILL.md +5 -0
  60. package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
  61. package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
  62. package/.ai-rules/skills/build-fix/SKILL.md +234 -0
  63. package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
  64. package/.ai-rules/skills/context-management/SKILL.md +1 -0
  65. package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
  66. package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
  67. package/.ai-rules/skills/database-migration/SKILL.md +1 -0
  68. package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
  69. package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
  70. package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
  71. package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
  72. package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
  73. package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
  74. package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
  75. package/.ai-rules/skills/git-master/SKILL.md +358 -0
  76. package/.ai-rules/skills/incident-response/SKILL.md +1 -0
  77. package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
  78. package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
  79. package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
  80. package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +198 -0
  81. package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
  82. package/.ai-rules/skills/onboard/SKILL.md +150 -0
  83. package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
  84. package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
  85. package/.ai-rules/skills/plan-to-issues/SKILL.md +318 -0
  86. package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
  87. package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
  88. package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
  89. package/.ai-rules/skills/pr-review/SKILL.md +4 -0
  90. package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
  91. package/.ai-rules/skills/refactoring/SKILL.md +1 -0
  92. package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
  93. package/.ai-rules/skills/retrospective/SKILL.md +192 -0
  94. package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
  95. package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
  96. package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
  97. package/.ai-rules/skills/security-audit/SKILL.md +4 -0
  98. package/.ai-rules/skills/ship/SKILL.md +242 -0
  99. package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
  100. package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
  101. package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
  102. package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
  103. package/.ai-rules/skills/skill-creator/assets/eval_review.html +568 -0
  104. package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
  105. package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
  106. package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
  107. package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
  108. package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
  109. package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
  110. package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
  111. package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
  112. package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
  113. package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
  114. package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
  115. package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
  116. package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
  117. package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
  118. package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
  119. package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
  120. package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
  121. package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
  122. package/bin/cli.js +170 -0
  123. package/lib/init/detect-stack.js +162 -0
  124. package/lib/init/generate-config.js +31 -0
  125. package/lib/init/index.js +86 -0
  126. package/lib/init/prompt.js +60 -0
  127. package/lib/init/scaffold.js +67 -0
  128. package/lib/init/suggest-agent.js +57 -0
  129. 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": "4.5.0",
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",