@1tool/js-boost 1.1.0 → 1.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 CHANGED
@@ -13,9 +13,11 @@ Instead of manually maintaining separate instruction files for each AI agent, yo
13
13
  ├── guidelines/
14
14
  │ ├── general.md ← coding conventions
15
15
  │ └── testing.md ← testing standards
16
- └── skills/
17
- └── my-skill/
18
- └── SKILL.md ← on-demand skill (loaded when relevant)
16
+ ├── skills/
17
+ └── my-skill/
18
+ └── SKILL.md ← on-demand skill (loaded when relevant)
19
+ └── mcp/
20
+ └── mcp.json ← MCP server definitions
19
21
  ```
20
22
 
21
23
  Run `npx @1tool/js-boost generate` and get the right file for each configured agent:
@@ -141,14 +143,11 @@ During `init` (and `agents`), installed agents are pre-selected automatically ba
141
143
 
142
144
  ## Configuration
143
145
 
144
- `js-boost.config.json` is managed by the CLI commands (`init`, `agents`, `mcp`) and committed to your repo.
146
+ MCP server definitions live in `.ai/mcp/mcp.json` (managed by `js-boost mcp`):
145
147
 
146
148
  ```json
147
149
  {
148
- "projectName": "my-app",
149
- "projectDescription": "",
150
- "agents": ["claude_code", "cursor", "codex"],
151
- "mcpServers": {
150
+ "servers": {
152
151
  "my-api": {
153
152
  "type": "remote",
154
153
  "url": "https://my-mcp.com/mcp",
@@ -161,7 +160,17 @@ During `init` (and `agents`), installed agents are pre-selected automatically ba
161
160
  "env": { "API_KEY": "secret" }
162
161
  }
163
162
  },
164
- "disableMcpServers": []
163
+ "disabled": []
164
+ }
165
+ ```
166
+
167
+ Agent selection and project metadata live in `js-boost.config.json` (managed by `init` / `agents`):
168
+
169
+ ```json
170
+ {
171
+ "projectName": "my-app",
172
+ "projectDescription": "",
173
+ "agents": ["claude_code", "cursor", "codex"]
165
174
  }
166
175
  ```
167
176
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1tool/js-boost",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Laravel Boost-inspired CLI for JavaScript projects — generates agent files from your .ai/ folder",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/cli.js CHANGED
@@ -88,7 +88,7 @@ program
88
88
  .option('--dir <path>', 'Project directory', process.cwd())
89
89
  .action(async (options) => {
90
90
  const projectDir = path.resolve(options.dir);
91
- const { readGuidelines, readSkills, readConfig } = await import('./utils/reader.js');
91
+ const { readGuidelines, readSkills, readConfig, readMcpConfig } = await import('./utils/reader.js');
92
92
  const { buildMcpServers } = await import('./utils/mcp.js');
93
93
  const { AGENTS, AGENTS_MD_CONSUMERS, MCP_JSON_CONSUMERS } = await import('./agents.js');
94
94
  const aiDir = path.join(projectDir, '.ai');
@@ -96,7 +96,7 @@ program
96
96
 
97
97
  const guidelines = await readGuidelines(aiDir);
98
98
  const skills = await readSkills(aiDir);
99
- const mcpServers = buildMcpServers(config);
99
+ const mcpServers = buildMcpServers(readMcpConfig(aiDir));
100
100
 
101
101
  const allAgentKeys = Object.keys(AGENTS);
102
102
  const activeAgents = new Set(config.agents ?? allAgentKeys);
@@ -2,11 +2,11 @@ import path from 'path';
2
2
  import chalk from 'chalk';
3
3
  import { text, select, multiselect, isCancel } from '@clack/prompts';
4
4
  import { DEFAULT_MCP_SERVERS } from '../utils/mcp.js';
5
- import { readConfig, writeFile } from '../utils/reader.js';
5
+ import { readMcpConfig, writeMcpConfig } from '../utils/reader.js';
6
6
 
7
- function displayServers(config) {
8
- const userServers = config.mcpServers || {};
9
- const disabled = new Set(config.disableMcpServers || []);
7
+ function displayServers(mcpConfig) {
8
+ const userServers = mcpConfig.servers || {};
9
+ const disabled = new Set(mcpConfig.disabled || []);
10
10
  const builtinKeys = Object.keys(DEFAULT_MCP_SERVERS);
11
11
 
12
12
  if (builtinKeys.length > 0) {
@@ -34,13 +34,13 @@ function displayServers(config) {
34
34
  console.log('');
35
35
  }
36
36
 
37
- async function addServer(config) {
37
+ async function addServer(mcpConfig) {
38
38
  const name = await text({
39
39
  message: 'Server key',
40
40
  placeholder: 'my-api',
41
41
  validate: (v) => {
42
42
  if (!v.trim()) return 'Key is required';
43
- if ((config.mcpServers || {})[v.trim()]) return `"${v.trim()}" already exists`;
43
+ if ((mcpConfig.servers || {})[v.trim()]) return `"${v.trim()}" already exists`;
44
44
  if (DEFAULT_MCP_SERVERS[v.trim()]) return `"${v.trim()}" is a built-in — use "Toggle built-ins" to enable/disable it`;
45
45
  if (!/^[a-z0-9_-]+$/.test(v.trim())) return 'Use only lowercase letters, numbers, hyphens, underscores';
46
46
  },
@@ -72,7 +72,7 @@ async function addServer(config) {
72
72
  });
73
73
  if (isCancel(description)) return;
74
74
 
75
- config.mcpServers[key] = {
75
+ mcpConfig.servers[key] = {
76
76
  type: 'remote',
77
77
  url: url.trim(),
78
78
  ...(description.trim() ? { description: description.trim() } : {}),
@@ -111,7 +111,7 @@ async function addServer(config) {
111
111
  }
112
112
  }
113
113
 
114
- config.mcpServers[key] = {
114
+ mcpConfig.servers[key] = {
115
115
  type: 'stdio',
116
116
  command: command.trim(),
117
117
  ...(args.length ? { args } : {}),
@@ -122,8 +122,8 @@ async function addServer(config) {
122
122
  console.log(` ${chalk.green('✓')} Added ${chalk.cyan(key)}`);
123
123
  }
124
124
 
125
- async function removeServer(config) {
126
- const keys = Object.keys(config.mcpServers);
125
+ async function removeServer(mcpConfig) {
126
+ const keys = Object.keys(mcpConfig.servers);
127
127
  if (keys.length === 0) {
128
128
  console.log(chalk.dim(' No custom servers to remove.'));
129
129
  return;
@@ -132,7 +132,7 @@ async function removeServer(config) {
132
132
  const toRemove = await multiselect({
133
133
  message: 'Select servers to remove',
134
134
  options: keys.map((k) => {
135
- const srv = config.mcpServers[k];
135
+ const srv = mcpConfig.servers[k];
136
136
  const addr = srv.type === 'remote'
137
137
  ? srv.url
138
138
  : `${srv.command}${srv.args?.length ? ' ' + srv.args.join(' ') : ''}`;
@@ -143,7 +143,7 @@ async function removeServer(config) {
143
143
  if (isCancel(toRemove)) return;
144
144
 
145
145
  for (const k of toRemove) {
146
- delete config.mcpServers[k];
146
+ delete mcpConfig.servers[k];
147
147
  }
148
148
 
149
149
  if (toRemove.length) {
@@ -151,14 +151,14 @@ async function removeServer(config) {
151
151
  }
152
152
  }
153
153
 
154
- async function toggleDefaults(config) {
154
+ async function toggleDefaults(mcpConfig) {
155
155
  const keys = Object.keys(DEFAULT_MCP_SERVERS);
156
156
  if (keys.length === 0) {
157
157
  console.log(chalk.dim(' No built-in servers configured.'));
158
158
  return;
159
159
  }
160
160
 
161
- const disabled = new Set(config.disableMcpServers || []);
161
+ const disabled = new Set(mcpConfig.disabled || []);
162
162
 
163
163
  const enabled = await multiselect({
164
164
  message: 'Which built-in servers should be enabled?',
@@ -173,14 +173,12 @@ async function toggleDefaults(config) {
173
173
  if (isCancel(enabled)) return;
174
174
 
175
175
  const enabledSet = new Set(enabled);
176
- config.disableMcpServers = keys.filter((k) => !enabledSet.has(k));
176
+ mcpConfig.disabled = keys.filter((k) => !enabledSet.has(k));
177
177
  }
178
178
 
179
179
  export async function configureMcp(projectDir) {
180
- const configPath = path.join(projectDir, 'js-boost.config.json');
181
- const config = readConfig(projectDir);
182
- config.mcpServers = config.mcpServers || {};
183
- config.disableMcpServers = config.disableMcpServers || [];
180
+ const aiDir = path.join(projectDir, '.ai');
181
+ const mcpConfig = readMcpConfig(aiDir);
184
182
 
185
183
  console.log('');
186
184
  console.log(chalk.bold.blue('⚡ js-boost') + chalk.dim(' — MCP server configuration'));
@@ -189,9 +187,9 @@ export async function configureMcp(projectDir) {
189
187
  let running = true;
190
188
 
191
189
  while (running) {
192
- displayServers(config);
190
+ displayServers(mcpConfig);
193
191
 
194
- const hasCustom = Object.keys(config.mcpServers).length > 0;
192
+ const hasCustom = Object.keys(mcpConfig.servers).length > 0;
195
193
  const hasBuiltins = Object.keys(DEFAULT_MCP_SERVERS).length > 0;
196
194
 
197
195
  const options = [
@@ -209,15 +207,15 @@ export async function configureMcp(projectDir) {
209
207
  }
210
208
 
211
209
  console.log('');
212
- if (action === 'add') await addServer(config);
213
- if (action === 'remove') await removeServer(config);
214
- if (action === 'toggle') await toggleDefaults(config);
210
+ if (action === 'add') await addServer(mcpConfig);
211
+ if (action === 'remove') await removeServer(mcpConfig);
212
+ if (action === 'toggle') await toggleDefaults(mcpConfig);
215
213
  console.log('');
216
214
  }
217
215
 
218
- writeFile(configPath, JSON.stringify(config, null, 2));
216
+ writeMcpConfig(aiDir, mcpConfig);
219
217
  console.log('');
220
- console.log(chalk.green(' ✓ Saved to js-boost.config.json'));
221
- console.log(chalk.dim(' Run `js-boost generate` to apply changes to agent files.'));
218
+ console.log(chalk.green(' ✓ Saved to .ai/mcp/mcp.json'));
219
+ console.log(chalk.dim(' Run `@1tool/js-boost generate` to apply changes to agent files.'));
222
220
  console.log('');
223
221
  }
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
- import { readGuidelines, readSkills, readConfig, writeFile } from './utils/reader.js';
3
+ import { readGuidelines, readSkills, readConfig, readMcpConfig, writeFile } from './utils/reader.js';
4
4
  import { buildMcpServers, generateMcpJson, generateJunieMcpJson } from './utils/mcp.js';
5
5
  import { AGENTS_MD_CONSUMERS, MCP_JSON_CONSUMERS } from './agents.js';
6
6
  import { generateAgentsMd } from './generators/agents.js';
@@ -33,7 +33,7 @@ export async function generate(projectDir, options = {}) {
33
33
  // 1. Read source files
34
34
  const guidelines = await readGuidelines(aiDir);
35
35
  const skills = await readSkills(aiDir);
36
- const mcpServers = buildMcpServers(config);
36
+ const mcpServers = buildMcpServers(readMcpConfig(aiDir));
37
37
 
38
38
  if (guidelines.length === 0 && skills.length === 0) {
39
39
  log(chalk.yellow(' ⚠ No guidelines or skills found in .ai/'));
package/src/init.js CHANGED
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import fs from 'fs';
3
3
  import chalk from 'chalk';
4
4
  import { multiselect, isCancel, cancel } from '@clack/prompts';
5
- import { writeFile } from './utils/reader.js';
5
+ import { writeFile, writeMcpConfig } from './utils/reader.js';
6
6
  import { AGENTS } from './agents.js';
7
7
  import { detectInstalledAgents } from './utils/detect.js';
8
8
 
@@ -102,6 +102,13 @@ export async function init(projectDir, options = {}) {
102
102
  }
103
103
  }
104
104
 
105
+ // Create .ai/mcp/mcp.json if it doesn't exist yet
106
+ const mcpJsonPath = path.join(aiDir, 'mcp', 'mcp.json');
107
+ if (!fs.existsSync(mcpJsonPath) || force) {
108
+ writeMcpConfig(aiDir, { servers: {}, disabled: [] });
109
+ console.log(` ${chalk.green('✓')} ${chalk.cyan('.ai/mcp/mcp.json')}`);
110
+ }
111
+
105
112
  // Create js-boost.config.json with defaults (or read existing)
106
113
  const configPath = path.join(projectDir, 'js-boost.config.json');
107
114
  let existingConfig = {};
@@ -116,8 +123,6 @@ export async function init(projectDir, options = {}) {
116
123
  projectName: existingConfig.projectName || path.basename(projectDir),
117
124
  projectDescription: existingConfig.projectDescription || '',
118
125
  agents: selectedAgents,
119
- mcpServers: existingConfig.mcpServers || {},
120
- disableMcpServers: existingConfig.disableMcpServers || [],
121
126
  };
122
127
 
123
128
  writeFile(configPath, JSON.stringify(config, null, 2));
package/src/utils/mcp.js CHANGED
@@ -8,9 +8,9 @@ export const DEFAULT_MCP_SERVERS = {
8
8
  * Build the MCP servers object, merging defaults with user-defined servers
9
9
  * from js-boost.config.json
10
10
  */
11
- export function buildMcpServers(config = {}) {
12
- const userServers = config.mcpServers || {};
13
- const disabledDefaults = config.disableMcpServers || [];
11
+ export function buildMcpServers(mcpConfig = {}) {
12
+ const userServers = mcpConfig.servers || {};
13
+ const disabledDefaults = mcpConfig.disabled || [];
14
14
 
15
15
  const servers = {};
16
16
 
@@ -74,6 +74,31 @@ export function readConfig(projectDir) {
74
74
  }
75
75
  }
76
76
 
77
+ /**
78
+ * Read .ai/mcp/mcp.json — MCP server definitions
79
+ * Returns { servers: {}, disabled: [] }
80
+ */
81
+ export function readMcpConfig(aiDir) {
82
+ const mcpPath = path.join(aiDir, 'mcp', 'mcp.json');
83
+ if (!fs.existsSync(mcpPath)) return { servers: {}, disabled: [] };
84
+ try {
85
+ const parsed = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
86
+ return {
87
+ servers: parsed.servers || {},
88
+ disabled: parsed.disabled || [],
89
+ };
90
+ } catch {
91
+ return { servers: {}, disabled: [] };
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Write .ai/mcp/mcp.json
97
+ */
98
+ export function writeMcpConfig(aiDir, mcpConfig) {
99
+ writeFile(path.join(aiDir, 'mcp', 'mcp.json'), JSON.stringify(mcpConfig, null, 2));
100
+ }
101
+
77
102
  /**
78
103
  * Ensure a directory exists
79
104
  */