qwen-base 1.0.2 → 1.0.4

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 (2) hide show
  1. package/bin/install.js +66 -8
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
6
  const readline = require('readline');
7
+ const { execSync } = require('child_process');
7
8
 
8
9
  const green = '\x1b[32m';
9
10
  const cyan = '\x1b[36m';
@@ -30,7 +31,6 @@ const args = process.argv.slice(2);
30
31
  const hasGlobal = args.includes('--global') || args.includes('-g');
31
32
  const hasLocal = args.includes('--local') || args.includes('-l');
32
33
  const hasHelp = args.includes('--help') || args.includes('-h');
33
- const skipTools = args.includes('--skip-tools');
34
34
 
35
35
  function parseConfigDirArg() {
36
36
  const idx = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
@@ -76,6 +76,18 @@ function countFiles(dir) {
76
76
  return count;
77
77
  }
78
78
 
79
+ function wireMcp(workspaceDir, mcpIndexPath) {
80
+ const mcpJsonPath = path.join(workspaceDir, '.mcp.json');
81
+ let mcpConfig = {};
82
+ if (fs.existsSync(mcpJsonPath)) {
83
+ try { mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8')); } catch (e) {}
84
+ }
85
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
86
+ const normalizedPath = mcpIndexPath.replace(/\\/g, '/');
87
+ mcpConfig.mcpServers['base-mcp'] = { command: 'node', args: [normalizedPath], type: 'stdio' };
88
+ fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
89
+ }
90
+
79
91
  console.log(banner);
80
92
 
81
93
  if (hasHelp) {
@@ -85,7 +97,6 @@ if (hasHelp) {
85
97
  ${cyan}-g, --global${reset} Install globally (to Qwen config directory)
86
98
  ${cyan}-l, --local${reset} Install locally (to ./.qwen/ in current directory)
87
99
  ${cyan}-c, --config-dir <path>${reset} Specify custom Qwen config directory
88
- ${cyan}--skip-tools${reset} Skip OSS analysis tool installation
89
100
  ${cyan}-h, --help${reset} Show this help message
90
101
 
91
102
  ${yellow}Examples:${reset}
@@ -98,6 +109,7 @@ if (hasHelp) {
98
109
  ${yellow}What gets installed:${reset}
99
110
  ${cyan}commands/qwen-base/${reset} 11 slash commands
100
111
  ${cyan}base/ Framework (core, transform, domains, schemas, rules, tools)
112
+ ${cyan}.mcp.json base-mcp server registration
101
113
  `);
102
114
  process.exit(0);
103
115
  }
@@ -107,26 +119,27 @@ function install(isGlobal) {
107
119
  const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.QWEN_CONFIG_DIR);
108
120
  const globalDir = configDir || path.join(os.homedir(), '.qwen');
109
121
  const qwenDir = isGlobal ? globalDir : path.join(process.cwd(), '.qwen');
110
- const aegisDest = path.join(qwenDir, 'aegis');
122
+ const baseDest = path.join(qwenDir, 'base');
111
123
  const cmdsDest = path.join(qwenDir, 'commands', 'qwen-base');
124
+ const workspaceRoot = isGlobal ? os.homedir() : process.cwd();
112
125
 
113
126
  const locationLabel = isGlobal
114
127
  ? qwenDir.replace(os.homedir(), '~')
115
128
  : qwenDir.replace(process.cwd(), '.');
116
129
 
117
- if (fs.existsSync(aegisDest) || fs.existsSync(cmdsDest)) {
130
+ if (fs.existsSync(baseDest) || fs.existsSync(cmdsDest)) {
118
131
  console.log(` ${yellow}Existing installation found at ${locationLabel}${reset}`);
119
132
  console.log(` Updating...`);
120
- if (fs.existsSync(aegisDest)) fs.rmSync(aegisDest, { recursive: true, force: true });
133
+ if (fs.existsSync(baseDest)) fs.rmSync(baseDest, { recursive: true, force: true });
121
134
  if (fs.existsSync(cmdsDest)) fs.rmSync(cmdsDest, { recursive: true, force: true });
122
135
  }
123
136
 
124
137
  console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
125
138
 
126
- // Copy framework (note: BASE uses 'src/' structure)
139
+ // Copy framework (src/ base/)
127
140
  if (fs.existsSync(path.join(src, 'src'))) {
128
- copyDir(path.join(src, 'src'), path.join(qwenDir, 'base'));
129
- console.log(` ${green}+${reset} base/ ${dim}(${countFiles(path.join(qwenDir, 'base'))} framework files)${reset}`);
141
+ copyDir(path.join(src, 'src'), baseDest);
142
+ console.log(` ${green}+${reset} base/ ${dim}(${countFiles(baseDest)} framework files)${reset}`);
130
143
  }
131
144
 
132
145
  // Copy commands
@@ -136,6 +149,51 @@ function install(isGlobal) {
136
149
  console.log(` ${green}+${reset} commands/qwen-base/ ${dim}(${countFiles(cmdsDest)} commands)${reset}`);
137
150
  }
138
151
 
152
+ // Wire BASE MCP server into .mcp.json
153
+ const mcpIndexPath = path.join(baseDest, 'packages', 'base-mcp', 'index.js');
154
+ if (fs.existsSync(mcpIndexPath)) {
155
+ wireMcp(workspaceRoot, mcpIndexPath);
156
+ console.log(` ${green}+${reset} Wired base-mcp in .mcp.json`);
157
+
158
+ // Install MCP dependencies
159
+ const mcpDir = path.join(baseDest, 'packages', 'base-mcp');
160
+ try {
161
+ execSync('npm install --production --silent', { cwd: mcpDir, stdio: 'pipe' });
162
+ console.log(` ${green}+${reset} MCP dependencies installed`);
163
+ } catch (e) {
164
+ console.log(` ${yellow}!${reset} MCP deps install failed — run cd ${mcpDir} && npm install${reset}`);
165
+ }
166
+ }
167
+
168
+ // Wire BASE Python hooks into settings.json
169
+ const settingsPath = path.join(qwenDir, 'settings.json');
170
+ let settings = {};
171
+ if (fs.existsSync(settingsPath)) {
172
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (e) {}
173
+ }
174
+ if (!settings.hooks) settings.hooks = {};
175
+
176
+ const hookFiles = [
177
+ { event: 'UserPromptSubmit', file: 'base-pulse-check.py' },
178
+ { event: 'UserPromptSubmit', file: 'psmm-injector.py' },
179
+ { event: 'SessionStart', file: 'satellite-detection.py' },
180
+ ];
181
+
182
+ for (const hook of hookFiles) {
183
+ const hookPath = path.join(baseDest, 'hooks', hook.file).replace(/\\/g, '/');
184
+ const hookCommand = `python3 ${hookPath}`;
185
+ if (!settings.hooks[hook.event]) settings.hooks[hook.event] = [];
186
+ const exists = settings.hooks[hook.event].some(h =>
187
+ (h.command && h.command.includes(hook.file)) ||
188
+ (h.hooks && h.hooks.some(i => i.command && i.command.includes(hook.file)))
189
+ );
190
+ if (!exists) {
191
+ settings.hooks[hook.event].push({ hooks: [{ type: 'command', command: hookCommand }] });
192
+ }
193
+ }
194
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
195
+ console.log(` ${green}+${reset} Wired BASE hooks in settings.json`);
196
+
139
197
  console.log(`
140
198
  ${green}Done!${reset} Open Qwen Code and type ${cyan}/base${reset} to start.
141
199
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwen-base",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Builder's Automated State Engine — workspace lifecycle management for Qwen Code",
5
5
  "bin": {
6
6
  "qwen-base": "bin/install.js"