codeninja 2.0.0 → 3.2.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 +122 -251
- package/agent/global-agent.md +8 -0
- package/cli.js +248 -223
- package/commands/debug.workflow.md +94 -0
- package/commands/explain.workflow.md +59 -0
- package/commands/optimize.workflow.md +124 -0
- package/commands/review.workflow.md +85 -0
- package/ide/antigravity/.agents/personas/database-architect.md +249 -0
- package/ide/antigravity/.agents/personas/global-orchestrator.md +144 -0
- package/ide/antigravity/.agents/personas/nodejs-backend.md +250 -0
- package/ide/antigravity/.agents/personas/reactjs-frontend.md +179 -0
- package/ide/antigravity/.agents/skills/api-builder/SKILL.md +179 -0
- package/ide/antigravity/.agents/skills/code-intelligence/SKILL.md +184 -0
- package/ide/antigravity/.agents/skills/database/SKILL.md +165 -0
- package/ide/antigravity/.agents/skills/mcp-and-context/SKILL.md +111 -0
- package/ide/antigravity/.agents/skills/reactjs/SKILL.md +211 -0
- package/ide/antigravity/.agents/workflows/codeninja-api.md +111 -0
- package/ide/antigravity/.agents/workflows/codeninja-audit.md +81 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-create.md +124 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +87 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-index.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +106 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +76 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-debug.md +82 -0
- package/ide/antigravity/.agents/workflows/codeninja-design.md +54 -0
- package/ide/antigravity/.agents/workflows/codeninja-explain.md +40 -0
- package/ide/antigravity/.agents/workflows/codeninja-init.md +336 -0
- package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +336 -0
- package/ide/antigravity/.agents/workflows/codeninja-modularize.md +216 -0
- package/ide/antigravity/.agents/workflows/codeninja-optimize.md +84 -0
- package/ide/antigravity/.agents/workflows/codeninja-refactor.md +68 -0
- package/ide/antigravity/.agents/workflows/codeninja-review.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-sync.md +183 -0
- package/ide/antigravity/.agents/workflows/codeninja-test.md +61 -0
- package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +250 -0
- package/ide/cursor/.cursor/mcp.json +8 -0
- package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +63 -0
- package/ide/cursor/.cursor/rules/02-mcp-and-context.mdc +38 -0
- package/ide/cursor/.cursor/rules/03-api-builder.mdc +124 -0
- package/ide/cursor/.cursor/rules/04-database.mdc +90 -0
- package/ide/cursor/.cursor/rules/05-reactjs.mdc +147 -0
- package/ide/cursor/.cursor/rules/06-code-intelligence.mdc +112 -0
- package/ide/vscode/.github/copilot-instructions.md +399 -0
- package/ide/vscode/.vscode/mcp.json +9 -0
- package/package.json +24 -23
package/cli.js
CHANGED
|
@@ -3,320 +3,330 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* codeninja CLI
|
|
5
5
|
* Installs the codeninja agent system into any project.
|
|
6
|
+
* Detects the active IDE and writes the appropriate structure.
|
|
6
7
|
*
|
|
7
8
|
* Usage:
|
|
8
|
-
* codeninja init
|
|
9
|
-
* codeninja
|
|
10
|
-
* codeninja
|
|
9
|
+
* codeninja init Auto-detect IDE and install
|
|
10
|
+
* codeninja init --ide=<ide> Force a specific IDE
|
|
11
|
+
* codeninja help Show available commands
|
|
12
|
+
* codeninja version Show installed version
|
|
13
|
+
*
|
|
14
|
+
* Supported IDEs:
|
|
15
|
+
* antigravity Google Antigravity IDE (.agents/ structure + slash commands)
|
|
16
|
+
* cursor Cursor IDE (.cursor/rules/ + .cursor/mcp.json)
|
|
17
|
+
* vscode VS Code / GitHub Copilot (.github/ + .vscode/mcp.json)
|
|
18
|
+
* all Install files for all three IDEs
|
|
11
19
|
*/
|
|
12
20
|
|
|
13
|
-
const fs
|
|
21
|
+
const fs = require('fs');
|
|
14
22
|
const path = require('path');
|
|
15
|
-
const os
|
|
23
|
+
const os = require('os');
|
|
16
24
|
const { execSync } = require('child_process');
|
|
17
25
|
|
|
18
|
-
const
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const command = args[0];
|
|
28
|
+
const ideFlag = (args.find(a => a.startsWith('--ide=')) || '').replace('--ide=', '');
|
|
29
|
+
|
|
19
30
|
const projectRoot = process.cwd();
|
|
20
|
-
const destDir
|
|
21
|
-
const srcDir
|
|
31
|
+
const destDir = path.join(projectRoot, '.codeninja');
|
|
32
|
+
const srcDir = path.dirname(__filename);
|
|
22
33
|
|
|
23
34
|
// ─── Command Router ───────────────────────────────────────────────────────────
|
|
24
35
|
|
|
25
|
-
if (!command || command === 'help') {
|
|
26
|
-
printHelp();
|
|
27
|
-
process.exit(0);
|
|
28
|
-
}
|
|
29
|
-
|
|
36
|
+
if (!command || command === 'help') { printHelp(); process.exit(0); }
|
|
30
37
|
if (command === 'version') {
|
|
31
38
|
const pkg = JSON.parse(fs.readFileSync(path.join(srcDir, 'package.json'), 'utf8'));
|
|
32
39
|
console.log('codeninja v' + pkg.version);
|
|
33
40
|
process.exit(0);
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
if (command === 'init') {
|
|
37
|
-
runInit();
|
|
38
|
-
process.exit(0);
|
|
39
|
-
}
|
|
42
|
+
if (command === 'init') { runInit(); process.exit(0); }
|
|
40
43
|
|
|
41
44
|
console.error('Unknown command: ' + command + '\n');
|
|
42
45
|
printHelp();
|
|
43
46
|
process.exit(1);
|
|
44
47
|
|
|
48
|
+
// ─── IDE Detection ────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Detects which IDE is likely active by checking for known config files and env vars.
|
|
52
|
+
* @returns {'antigravity'|'cursor'|'vscode'|'unknown'}
|
|
53
|
+
*/
|
|
54
|
+
function detectIDE() {
|
|
55
|
+
// Antigravity: ~/.gemini/antigravity/ directory exists
|
|
56
|
+
if (process.env.ANTIGRAVITY_IDE ||
|
|
57
|
+
fs.existsSync(path.join(os.homedir(), '.gemini', 'antigravity'))) {
|
|
58
|
+
return 'antigravity';
|
|
59
|
+
}
|
|
60
|
+
// Cursor: ~/.cursor/ directory or Cursor-specific env vars
|
|
61
|
+
if (process.env.CURSOR_TRACE || process.env.CURSOR_SESSION_ID ||
|
|
62
|
+
fs.existsSync(path.join(os.homedir(), '.cursor'))) {
|
|
63
|
+
return 'cursor';
|
|
64
|
+
}
|
|
65
|
+
// VS Code: VSCODE_PID, terminal integration, or ~/.vscode/ exists
|
|
66
|
+
if (process.env.VSCODE_PID || process.env.TERM_PROGRAM === 'vscode' ||
|
|
67
|
+
fs.existsSync(path.join(os.homedir(), '.vscode'))) {
|
|
68
|
+
return 'vscode';
|
|
69
|
+
}
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
|
|
45
73
|
// ─── Init ─────────────────────────────────────────────────────────────────────
|
|
46
74
|
|
|
47
75
|
function runInit() {
|
|
48
|
-
|
|
76
|
+
let targetIDE = ideFlag || detectIDE();
|
|
77
|
+
const installAll = targetIDE === 'all';
|
|
78
|
+
|
|
79
|
+
if (!ideFlag && targetIDE === 'unknown') {
|
|
80
|
+
console.log('\ncodeninja could not auto-detect your IDE.');
|
|
81
|
+
console.log('Installing for all supported IDEs instead.');
|
|
82
|
+
console.log('(Use --ide=antigravity | --ide=cursor | --ide=vscode to be specific)\n');
|
|
83
|
+
targetIDE = 'all';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const detectedLabel = ideFlag ? 'forced via --ide' : 'auto-detected';
|
|
87
|
+
console.log('\ncodeninja — installing into ' + projectRoot);
|
|
88
|
+
console.log('IDE: ' + (installAll ? 'all (antigravity + cursor + vscode)' : targetIDE)
|
|
89
|
+
+ ' (' + detectedLabel + ')\n');
|
|
49
90
|
|
|
50
91
|
if (fs.existsSync(destDir)) {
|
|
51
|
-
console.log('codeninja is already installed
|
|
52
|
-
console.log('If you want to reinstall, delete the .codeninja folder first.\n');
|
|
92
|
+
console.log('codeninja is already installed. Delete .codeninja/ first to reinstall.\n');
|
|
53
93
|
return;
|
|
54
94
|
}
|
|
55
95
|
|
|
56
|
-
// Absolute path to mcp-server.js — used for Antigravity and Claude Desktop
|
|
57
|
-
// because they do not support relative or workspace-variable paths.
|
|
58
96
|
const mcpServerAbsPath = path.join(destDir, 'mcp-server.js');
|
|
59
|
-
|
|
60
|
-
// Ensure .codeninja root exists
|
|
61
97
|
fs.mkdirSync(destDir, { recursive: true });
|
|
62
98
|
|
|
63
|
-
// ──
|
|
64
|
-
console.log('[ 1/
|
|
65
|
-
copyDir(path.join(srcDir, 'agent'),
|
|
99
|
+
// ── 1: Core agent files ────────────────────────────────────────────────
|
|
100
|
+
console.log('[ 1/6 ] Copying core agent files...');
|
|
101
|
+
copyDir(path.join(srcDir, 'agent'), path.join(destDir, 'agent'));
|
|
66
102
|
copyDir(path.join(srcDir, 'commands'), path.join(destDir, 'commands'));
|
|
67
|
-
copyDir(path.join(srcDir, 'tasks'),
|
|
68
|
-
|
|
69
|
-
fs.copyFileSync(
|
|
70
|
-
path.join(srcDir, 'mcp-server.js'),
|
|
71
|
-
path.join(destDir, 'mcp-server.js')
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const innerPkg = {
|
|
75
|
-
name: 'codeninja-mcp',
|
|
76
|
-
private: true,
|
|
77
|
-
dependencies: { '@modelcontextprotocol/sdk': '^1.0.0' }
|
|
78
|
-
};
|
|
103
|
+
copyDir(path.join(srcDir, 'tasks'), path.join(destDir, 'tasks'));
|
|
104
|
+
fs.copyFileSync(path.join(srcDir, 'mcp-server.js'), path.join(destDir, 'mcp-server.js'));
|
|
79
105
|
fs.writeFileSync(
|
|
80
106
|
path.join(destDir, 'package.json'),
|
|
81
|
-
JSON.stringify(
|
|
107
|
+
JSON.stringify({ name: 'codeninja-mcp', private: true,
|
|
108
|
+
dependencies: { '@modelcontextprotocol/sdk': '^1.0.0' } }, null, 2),
|
|
82
109
|
'utf8'
|
|
83
110
|
);
|
|
84
111
|
|
|
85
|
-
// ──
|
|
86
|
-
console.log('[ 2/
|
|
112
|
+
// ── 2: Context directory ───────────────────────────────────────────────
|
|
113
|
+
console.log('[ 2/6 ] Creating context directory...');
|
|
87
114
|
fs.mkdirSync(path.join(destDir, 'context'), { recursive: true });
|
|
88
115
|
|
|
89
|
-
// ──
|
|
90
|
-
console.log('[ 3/
|
|
116
|
+
// ── 3: Install MCP SDK ─────────────────────────────────────────────────
|
|
117
|
+
console.log('[ 3/6 ] Installing MCP SDK...');
|
|
91
118
|
try {
|
|
92
119
|
execSync('npm install', { cwd: destDir, stdio: 'inherit' });
|
|
93
120
|
} catch (e) {
|
|
94
|
-
console.error('\nERROR: npm install failed inside .codeninja
|
|
95
|
-
console.error('Make sure npm is installed and you have internet access.');
|
|
96
|
-
console.error(e.message);
|
|
121
|
+
console.error('\nERROR: npm install failed inside .codeninja/\n' + e.message);
|
|
97
122
|
process.exit(1);
|
|
98
123
|
}
|
|
99
124
|
|
|
100
|
-
// ──
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
//
|
|
133
|
-
console.log('[ 6/
|
|
134
|
-
const antigravityConfigPath = path.join(
|
|
135
|
-
os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'
|
|
136
|
-
);
|
|
137
|
-
writeAntigravityConfig(antigravityConfigPath, mcpServerAbsPath);
|
|
138
|
-
|
|
139
|
-
// ── Step 7: Update .gitignore if it already exists ────────────────────────
|
|
140
|
-
// We never create a .gitignore — that is the project's responsibility.
|
|
141
|
-
// The agent's generate-gitignore task already includes .codeninja/node_modules/
|
|
142
|
-
// in the .gitignore it writes during @initialize-project.
|
|
143
|
-
// This step handles the case where codeninja init runs on a project that
|
|
144
|
-
// already has a .gitignore but has NOT yet run @initialize-project.
|
|
145
|
-
console.log('[ 7/7 ] Checking .gitignore...');
|
|
125
|
+
// ── 4: IDE-specific project files ─────────────────────────────────────
|
|
126
|
+
console.log('\n[ 4/6 ] Writing IDE-specific files...');
|
|
127
|
+
const ides = installAll ? ['antigravity', 'cursor', 'vscode'] : [targetIDE];
|
|
128
|
+
for (const ide of ides) {
|
|
129
|
+
console.log('\n ── ' + ide.toUpperCase() + ' ──');
|
|
130
|
+
if (ide === 'antigravity') installAntigravityFiles();
|
|
131
|
+
else if (ide === 'cursor') installCursorFiles();
|
|
132
|
+
else if (ide === 'vscode') installVSCodeFiles();
|
|
133
|
+
else console.log(' Unknown IDE: ' + ide + ' — skipped');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── 5: Global MCP configs ──────────────────────────────────────────────
|
|
137
|
+
console.log('\n[ 5/6 ] Writing global MCP configs...');
|
|
138
|
+
if (installAll || targetIDE === 'vscode') {
|
|
139
|
+
writeGlobalMcpConfig(
|
|
140
|
+
path.join(os.homedir(), '.vscode', 'mcp.json'),
|
|
141
|
+
'servers', { type: 'stdio', command: 'node', args: [mcpServerAbsPath] }, 'VS Code'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (installAll || targetIDE === 'cursor') {
|
|
145
|
+
writeGlobalMcpConfig(
|
|
146
|
+
path.join(os.homedir(), '.cursor', 'mcp.json'),
|
|
147
|
+
'mcpServers', { command: 'node', args: [mcpServerAbsPath] }, 'Cursor'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
if (installAll || targetIDE === 'antigravity') {
|
|
151
|
+
writeAntigravityConfig(
|
|
152
|
+
path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'),
|
|
153
|
+
mcpServerAbsPath
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── 6: .gitignore ──────────────────────────────────────────────────────
|
|
158
|
+
console.log('\n[ 6/6 ] Checking .gitignore...');
|
|
146
159
|
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
147
|
-
const
|
|
160
|
+
const giEntry = '\n# codeninja — MCP server dependencies (do not commit)\n.codeninja/node_modules/\n';
|
|
148
161
|
if (fs.existsSync(gitignorePath)) {
|
|
149
162
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
150
163
|
if (!content.includes('.codeninja/node_modules')) {
|
|
151
|
-
fs.appendFileSync(gitignorePath,
|
|
164
|
+
fs.appendFileSync(gitignorePath, giEntry, 'utf8');
|
|
152
165
|
console.log(' .gitignore updated ✓');
|
|
153
166
|
} else {
|
|
154
|
-
console.log(' .gitignore already
|
|
167
|
+
console.log(' .gitignore already up to date — skipped');
|
|
155
168
|
}
|
|
156
169
|
} else {
|
|
157
|
-
console.log(' .gitignore
|
|
158
|
-
console.log(' (
|
|
170
|
+
console.log(' No .gitignore found — skipped');
|
|
171
|
+
console.log(' (/codeninja:init will add this automatically)');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
printInstallSummary(mcpServerAbsPath, ides);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ─── IDE file installers ──────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
function installAntigravityFiles() {
|
|
180
|
+
const src = path.join(srcDir, 'ide', 'antigravity', '.agents');
|
|
181
|
+
const dest = path.join(projectRoot, '.agents');
|
|
182
|
+
if (!fs.existsSync(src)) { console.log(' [SKIP] ide/antigravity source not found'); return; }
|
|
183
|
+
copyDir(src, dest);
|
|
184
|
+
const personaCount = countFiles(path.join(dest, 'personas'));
|
|
185
|
+
const skillCount = countDirs(path.join(dest, 'skills'));
|
|
186
|
+
const wfCount = countFiles(path.join(dest, 'workflows'));
|
|
187
|
+
console.log(' .agents/personas/ (' + personaCount + ' personas) ✓');
|
|
188
|
+
console.log(' global-orchestrator · nodejs-backend · database-architect · reactjs-frontend');
|
|
189
|
+
console.log(' .agents/skills/ (' + skillCount + ' skill domains) ✓');
|
|
190
|
+
console.log(' mcp-and-context · api-builder · database · reactjs · code-intelligence');
|
|
191
|
+
console.log(' .agents/workflows/ (' + wfCount + ' slash commands) ✓');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function installCursorFiles() {
|
|
195
|
+
const src = path.join(srcDir, 'ide', 'cursor', '.cursor');
|
|
196
|
+
const dest = path.join(projectRoot, '.cursor');
|
|
197
|
+
if (!fs.existsSync(src)) { console.log(' [SKIP] ide/cursor source not found'); return; }
|
|
198
|
+
copyDir(src, dest);
|
|
199
|
+
const ruleCount = countFiles(path.join(dest, 'rules'));
|
|
200
|
+
console.log(' .cursor/rules/ (' + ruleCount + ' scoped rule files) ✓');
|
|
201
|
+
console.log(' 01-global-orchestrator · 02-mcp-and-context · 03-api-builder');
|
|
202
|
+
console.log(' 04-database · 05-reactjs · 06-code-intelligence');
|
|
203
|
+
console.log(' .cursor/mcp.json ✓');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function installVSCodeFiles() {
|
|
207
|
+
const ideSrc = path.join(srcDir, 'ide', 'vscode');
|
|
208
|
+
|
|
209
|
+
const githubSrc = path.join(ideSrc, '.github');
|
|
210
|
+
const githubDest = path.join(projectRoot, '.github');
|
|
211
|
+
if (fs.existsSync(githubSrc)) {
|
|
212
|
+
copyDir(githubSrc, githubDest);
|
|
213
|
+
console.log(' .github/copilot-instructions.md ✓');
|
|
159
214
|
}
|
|
160
215
|
|
|
161
|
-
|
|
162
|
-
|
|
216
|
+
const vscodeSrc = path.join(ideSrc, '.vscode');
|
|
217
|
+
const vscodeDest = path.join(projectRoot, '.vscode');
|
|
218
|
+
if (fs.existsSync(vscodeSrc)) {
|
|
219
|
+
fs.mkdirSync(vscodeDest, { recursive: true });
|
|
220
|
+
const mcpDest = path.join(vscodeDest, 'mcp.json');
|
|
221
|
+
if (!fs.existsSync(mcpDest)) {
|
|
222
|
+
fs.copyFileSync(path.join(vscodeSrc, 'mcp.json'), mcpDest);
|
|
223
|
+
console.log(' .vscode/mcp.json ✓');
|
|
224
|
+
} else {
|
|
225
|
+
console.log(' .vscode/mcp.json already exists — skipped');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
163
228
|
}
|
|
164
229
|
|
|
165
|
-
// ───
|
|
230
|
+
// ─── Config Writers ───────────────────────────────────────────────────────────
|
|
166
231
|
|
|
167
232
|
function writeGlobalMcpConfig(configPath, serversKey, entry, ideName) {
|
|
168
233
|
try {
|
|
169
234
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
170
|
-
|
|
171
235
|
let existing = {};
|
|
172
|
-
|
|
173
236
|
if (fs.existsSync(configPath)) {
|
|
174
|
-
try {
|
|
175
|
-
|
|
176
|
-
} catch {
|
|
177
|
-
console.log(' Warning: existing ' + path.basename(configPath) + ' could not be parsed — will overwrite.');
|
|
178
|
-
}
|
|
237
|
+
try { existing = JSON.parse(fs.readFileSync(configPath, 'utf8')); }
|
|
238
|
+
catch { console.log(' Warning: ' + path.basename(configPath) + ' could not be parsed — overwriting.'); }
|
|
179
239
|
}
|
|
180
|
-
|
|
181
240
|
if (!existing[serversKey]) existing[serversKey] = {};
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
console.log(' ' + ideName + ' global config already has codeninja entry — skipped');
|
|
241
|
+
const cur = existing[serversKey].codeninja;
|
|
242
|
+
if (cur && JSON.stringify(cur.args) === JSON.stringify(entry.args)) {
|
|
243
|
+
console.log(' ' + ideName + ' global config already current — skipped');
|
|
186
244
|
return;
|
|
187
245
|
}
|
|
188
|
-
|
|
189
246
|
existing[serversKey].codeninja = entry;
|
|
190
247
|
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), 'utf8');
|
|
191
|
-
console.log(' ' + configPath + '
|
|
248
|
+
console.log(' ' + configPath + ' ✓');
|
|
192
249
|
} catch (e) {
|
|
193
|
-
console.log(' ' + ideName + ' global config skipped
|
|
250
|
+
console.log(' ' + ideName + ' global config skipped: ' + e.message);
|
|
194
251
|
}
|
|
195
252
|
}
|
|
196
253
|
|
|
197
|
-
// ─── Antigravity Config Writer ────────────────────────────────────────────────
|
|
198
|
-
|
|
199
254
|
function writeAntigravityConfig(configPath, mcpServerAbsPath) {
|
|
200
|
-
const newEntry = {
|
|
201
|
-
command: 'node',
|
|
202
|
-
args: [mcpServerAbsPath]
|
|
203
|
-
};
|
|
204
|
-
|
|
205
255
|
try {
|
|
206
256
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
207
|
-
|
|
208
257
|
let existing = { mcpServers: {} };
|
|
209
|
-
|
|
210
|
-
// If the file already exists, read and parse it safely.
|
|
211
|
-
// We deep-merge — never overwrite other entries the developer has added.
|
|
212
258
|
if (fs.existsSync(configPath)) {
|
|
213
|
-
try {
|
|
214
|
-
|
|
215
|
-
const parsed = JSON.parse(raw);
|
|
216
|
-
existing = parsed;
|
|
217
|
-
} catch {
|
|
218
|
-
// File is corrupt or empty — start fresh but preserve the file
|
|
219
|
-
console.log(' Warning: existing mcp_config.json could not be parsed — will overwrite.');
|
|
220
|
-
}
|
|
259
|
+
try { existing = JSON.parse(fs.readFileSync(configPath, 'utf8')); }
|
|
260
|
+
catch { console.log(' Warning: mcp_config.json parse failed — overwriting.'); }
|
|
221
261
|
}
|
|
222
|
-
|
|
223
262
|
if (!existing.mcpServers) existing.mcpServers = {};
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (current && current.args && current.args[0] === mcpServerAbsPath) {
|
|
228
|
-
console.log(' Antigravity config already has codeninja entry — skipped');
|
|
263
|
+
const cur = existing.mcpServers.codeninja;
|
|
264
|
+
if (cur && cur.args && cur.args[0] === mcpServerAbsPath) {
|
|
265
|
+
console.log(' Antigravity config already current — skipped');
|
|
229
266
|
return;
|
|
230
267
|
}
|
|
231
|
-
|
|
232
|
-
// Write our entry (adds or updates only the codeninja key)
|
|
233
|
-
existing.mcpServers.codeninja = newEntry;
|
|
234
|
-
|
|
268
|
+
existing.mcpServers.codeninja = { command: 'node', args: [mcpServerAbsPath] };
|
|
235
269
|
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), 'utf8');
|
|
236
|
-
console.log(' ~/.gemini/antigravity/mcp_config.json
|
|
237
|
-
|
|
270
|
+
console.log(' ~/.gemini/antigravity/mcp_config.json ✓');
|
|
238
271
|
} catch (e) {
|
|
239
|
-
|
|
240
|
-
console.log('
|
|
241
|
-
console.log(' (This is fine if you are not using Antigravity IDE)');
|
|
272
|
+
console.log(' Antigravity config skipped: ' + e.message);
|
|
273
|
+
console.log(' (OK if Antigravity is not installed)');
|
|
242
274
|
}
|
|
243
275
|
}
|
|
244
276
|
|
|
245
277
|
// ─── Summary ──────────────────────────────────────────────────────────────────
|
|
246
278
|
|
|
247
|
-
function printInstallSummary(mcpServerAbsPath) {
|
|
279
|
+
function printInstallSummary(mcpServerAbsPath, ides) {
|
|
248
280
|
const platform = process.platform;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
281
|
+
const claudeConfigPath = platform === 'win32'
|
|
282
|
+
? path.join(process.env.APPDATA || '%APPDATA%', 'Claude', 'claude_desktop_config.json')
|
|
283
|
+
: platform === 'darwin'
|
|
284
|
+
? path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
|
|
285
|
+
: path.join(os.homedir(), '.config', 'claude', 'claude_desktop_config.json');
|
|
286
|
+
|
|
287
|
+
let ideNotes = '';
|
|
288
|
+
if (ides.includes('antigravity')) ideNotes += `
|
|
289
|
+
Antigravity → ... → MCP Servers → Manage MCP Servers → Refresh
|
|
290
|
+
Structure installed:
|
|
291
|
+
.agents/personas/ 4 personas (global-orchestrator, nodejs-backend, database-architect, reactjs-frontend)
|
|
292
|
+
.agents/skills/ 5 skill domains (mcp-and-context, api-builder, database, reactjs, code-intelligence)
|
|
293
|
+
.agents/workflows/ 20 slash commands
|
|
294
|
+
Slash commands: /codeninja:init /codeninja:api /codeninja:design /codeninja:audit
|
|
295
|
+
/codeninja:test /codeninja:refactor /codeninja:sync
|
|
296
|
+
/codeninja:explain /codeninja:review /codeninja:debug /codeninja:optimize
|
|
297
|
+
/codeninja:db:create /codeninja:db:modify /codeninja:db:index
|
|
298
|
+
/codeninja:db:drop /codeninja:db:seed /codeninja:db:sync
|
|
299
|
+
@modularize @validate-page @integrate-api
|
|
300
|
+
`;
|
|
301
|
+
if (ides.includes('cursor')) ideNotes += `
|
|
302
|
+
Cursor → reopen project → use /codeninja:* in AI Chat
|
|
303
|
+
Rules installed: 6 scoped .mdc files (auto-loaded per file type)
|
|
304
|
+
01-global-orchestrator 02-mcp-and-context 03-api-builder
|
|
305
|
+
04-database 05-reactjs 06-code-intelligence
|
|
306
|
+
`;
|
|
307
|
+
if (ides.includes('vscode')) ideNotes += `
|
|
308
|
+
VS Code → reopen project → use @workspace /codeninja:* in Copilot Chat
|
|
309
|
+
`;
|
|
259
310
|
|
|
260
311
|
console.log(`
|
|
261
312
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
262
313
|
codeninja installed successfully
|
|
263
314
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
264
|
-
|
|
265
|
-
✓ .codeninja/ agent files installed
|
|
266
|
-
✓ ~/.vscode/mcp.json VS Code configured (global)
|
|
267
|
-
✓ ~/.cursor/mcp.json Cursor configured (global)
|
|
268
|
-
✓ ~/.gemini/antigravity/ Antigravity configured (global)
|
|
269
|
-
|
|
315
|
+
${ideNotes}
|
|
270
316
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
271
|
-
CLAUDE DESKTOP (manual
|
|
317
|
+
CLAUDE DESKTOP (manual — one time only)
|
|
272
318
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
273
319
|
|
|
274
|
-
|
|
275
|
-
${claudeConfigPath}
|
|
276
|
-
|
|
277
|
-
Add this inside the "mcpServers" object:
|
|
320
|
+
File: ${claudeConfigPath}
|
|
278
321
|
|
|
322
|
+
Add inside "mcpServers":
|
|
279
323
|
"codeninja": {
|
|
280
324
|
"command": "node",
|
|
281
325
|
"args": ["${mcpServerAbsPath}"]
|
|
282
326
|
}
|
|
283
327
|
|
|
284
|
-
Save and restart Claude Desktop.
|
|
285
|
-
|
|
286
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
287
|
-
IDE NOTES
|
|
288
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
289
|
-
|
|
290
|
-
VS Code — reopen this project to activate the MCP server
|
|
291
|
-
Cursor — reopen this project to activate the MCP server
|
|
292
|
-
Antigravity — go to ... → MCP Servers → Manage MCP Servers
|
|
293
|
-
and click Refresh to pick up the new entry
|
|
294
|
-
|
|
295
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
296
|
-
AVAILABLE COMMANDS (type in your AI chat)
|
|
297
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
298
|
-
|
|
299
|
-
@initialize-project Bootstrap a new service or database
|
|
300
|
-
@create-api Add a new API module to a service
|
|
301
|
-
@design Plan a feature before coding
|
|
302
|
-
@audit Security and quality review
|
|
303
|
-
@test Generate Jest test files
|
|
304
|
-
@refactor Rename with full context tracking
|
|
305
|
-
@sync Rebuild context from repo
|
|
306
|
-
|
|
307
|
-
Database commands:
|
|
308
|
-
@db:create-table Design and generate a new table
|
|
309
|
-
@db:modify-table Add / rename / drop a column
|
|
310
|
-
@db:add-index Add a new index
|
|
311
|
-
@db:drop-table Generate a DROP migration
|
|
312
|
-
@db:seed Add seed data
|
|
313
|
-
@db:sync Rebuild DB schema in context
|
|
314
|
-
|
|
315
328
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
316
|
-
|
|
317
|
-
Next step: open this project in your IDE,
|
|
318
|
-
then type @initialize-project to get started.
|
|
319
|
-
|
|
329
|
+
Next: open project in your IDE → /codeninja:init
|
|
320
330
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
321
331
|
`);
|
|
322
332
|
}
|
|
@@ -326,27 +336,42 @@ function printHelp() {
|
|
|
326
336
|
codeninja — AI agent scaffolding system
|
|
327
337
|
|
|
328
338
|
Usage:
|
|
329
|
-
codeninja init
|
|
330
|
-
codeninja
|
|
331
|
-
codeninja
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
339
|
+
codeninja init Install (auto-detects IDE)
|
|
340
|
+
codeninja init --ide=<ide> Force a specific IDE
|
|
341
|
+
codeninja version Show version
|
|
342
|
+
codeninja help Show this help
|
|
343
|
+
|
|
344
|
+
IDE values for --ide:
|
|
345
|
+
antigravity → .agents/ structure (slash commands)
|
|
346
|
+
cursor → .cursor/rules/ + mcp.json
|
|
347
|
+
vscode → .github/copilot-instructions.md + .vscode/mcp.json
|
|
348
|
+
all → all three IDEs
|
|
349
|
+
|
|
350
|
+
Examples:
|
|
351
|
+
codeninja init
|
|
352
|
+
codeninja init --ide=antigravity
|
|
353
|
+
codeninja init --ide=all
|
|
335
354
|
`);
|
|
336
355
|
}
|
|
337
356
|
|
|
338
|
-
// ─── Helpers
|
|
357
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
339
358
|
|
|
340
359
|
function copyDir(src, dest) {
|
|
341
360
|
if (!fs.existsSync(src)) return;
|
|
342
361
|
fs.mkdirSync(dest, { recursive: true });
|
|
343
362
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
344
|
-
const
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
copyDir(srcPath, destPath);
|
|
348
|
-
} else {
|
|
349
|
-
fs.copyFileSync(srcPath, destPath);
|
|
350
|
-
}
|
|
363
|
+
const s = path.join(src, entry.name);
|
|
364
|
+
const d = path.join(dest, entry.name);
|
|
365
|
+
entry.isDirectory() ? copyDir(s, d) : fs.copyFileSync(s, d);
|
|
351
366
|
}
|
|
352
|
-
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function countFiles(dir) {
|
|
370
|
+
if (!fs.existsSync(dir)) return 0;
|
|
371
|
+
return fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isFile()).length;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function countDirs(dir) {
|
|
375
|
+
if (!fs.existsSync(dir)) return 0;
|
|
376
|
+
return fs.readdirSync(dir).filter(f => fs.statSync(path.join(dir, f)).isDirectory()).length;
|
|
377
|
+
}
|