qwen-seed 1.0.1 → 1.0.2
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/bin/install.js +106 -101
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
+
const readline = require('readline');
|
|
6
7
|
|
|
7
8
|
// Colors
|
|
8
9
|
const green = '\x1b[32m';
|
|
@@ -11,32 +12,30 @@ const yellow = '\x1b[33m';
|
|
|
11
12
|
const dim = '\x1b[2m';
|
|
12
13
|
const reset = '\x1b[0m';
|
|
13
14
|
|
|
14
|
-
// Get version from package.json
|
|
15
15
|
const pkg = require('../package.json');
|
|
16
16
|
|
|
17
17
|
const banner = `
|
|
18
18
|
${green} \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
19
|
-
\u2588\u2588\u2554\u2550\u2550\
|
|
20
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\
|
|
21
|
-
\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\
|
|
22
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d\u2588\u2588\
|
|
23
|
-
\u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\
|
|
19
|
+
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d
|
|
20
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557
|
|
21
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255d
|
|
22
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
23
|
+
\u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u2550\u2550\u2550\u2550\u255d${reset}
|
|
24
24
|
|
|
25
25
|
SEED ${dim}v${pkg.version}${reset}
|
|
26
26
|
Structured Evaluation & Engineering Design
|
|
27
27
|
for Qwen Code
|
|
28
28
|
`;
|
|
29
29
|
|
|
30
|
-
// Parse args
|
|
31
30
|
const args = process.argv.slice(2);
|
|
32
|
-
const
|
|
31
|
+
const hasGlobal = args.includes('--global') || args.includes('-g');
|
|
33
32
|
const hasLocal = args.includes('--local') || args.includes('-l');
|
|
33
|
+
const hasHelp = args.includes('--help') || args.includes('-h');
|
|
34
34
|
|
|
35
|
-
// Parse --config-dir argument
|
|
36
35
|
function parseConfigDirArg() {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
const nextArg = args[
|
|
36
|
+
const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
|
|
37
|
+
if (configDirIndex !== -1) {
|
|
38
|
+
const nextArg = args[configDirIndex + 1];
|
|
40
39
|
if (!nextArg || nextArg.startsWith('-')) {
|
|
41
40
|
console.error(` ${yellow}--config-dir requires a path argument${reset}`);
|
|
42
41
|
process.exit(1);
|
|
@@ -44,25 +43,17 @@ function parseConfigDirArg() {
|
|
|
44
43
|
return nextArg;
|
|
45
44
|
}
|
|
46
45
|
const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
|
|
47
|
-
if (configDirArg)
|
|
48
|
-
return configDirArg.split('=')[1];
|
|
49
|
-
}
|
|
46
|
+
if (configDirArg) return configDirArg.split('=')[1];
|
|
50
47
|
return null;
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
50
|
+
const explicitConfigDir = parseConfigDirArg();
|
|
51
|
+
|
|
56
52
|
function expandTilde(filePath) {
|
|
57
|
-
if (filePath && filePath.startsWith('~/'))
|
|
58
|
-
return path.join(os.homedir(), filePath.slice(2));
|
|
59
|
-
}
|
|
53
|
+
if (filePath && filePath.startsWith('~/')) return path.join(os.homedir(), filePath.slice(2));
|
|
60
54
|
return filePath;
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
/**
|
|
64
|
-
* Recursively copy directory, skipping .paul/, .git/, node_modules/, bin/
|
|
65
|
-
*/
|
|
66
57
|
function copyDir(srcDir, destDir, skipDirs = []) {
|
|
67
58
|
fs.mkdirSync(destDir, { recursive: true });
|
|
68
59
|
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
@@ -70,45 +61,36 @@ function copyDir(srcDir, destDir, skipDirs = []) {
|
|
|
70
61
|
if (skipDirs.includes(entry.name)) continue;
|
|
71
62
|
const srcPath = path.join(srcDir, entry.name);
|
|
72
63
|
const destPath = path.join(destDir, entry.name);
|
|
73
|
-
if (entry.isDirectory())
|
|
74
|
-
|
|
75
|
-
} else {
|
|
76
|
-
fs.copyFileSync(srcPath, destPath);
|
|
77
|
-
}
|
|
64
|
+
if (entry.isDirectory()) copyDir(srcPath, destPath, skipDirs);
|
|
65
|
+
else fs.copyFileSync(srcPath, destPath);
|
|
78
66
|
}
|
|
79
67
|
}
|
|
80
68
|
|
|
81
|
-
/**
|
|
82
|
-
* Count files recursively
|
|
83
|
-
*/
|
|
84
69
|
function countFiles(dir, ext) {
|
|
85
70
|
let count = 0;
|
|
86
71
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
87
72
|
for (const entry of entries) {
|
|
88
73
|
const fullPath = path.join(dir, entry.name);
|
|
89
|
-
if (entry.isDirectory())
|
|
90
|
-
|
|
91
|
-
} else if (!ext || entry.name.endsWith(ext)) {
|
|
92
|
-
count++;
|
|
93
|
-
}
|
|
74
|
+
if (entry.isDirectory()) count += countFiles(fullPath, ext);
|
|
75
|
+
else if (!ext || entry.name.endsWith(ext)) count++;
|
|
94
76
|
}
|
|
95
77
|
return count;
|
|
96
78
|
}
|
|
97
79
|
|
|
98
80
|
console.log(banner);
|
|
99
81
|
|
|
100
|
-
// Show help
|
|
101
82
|
if (hasHelp) {
|
|
102
83
|
console.log(` ${yellow}Usage:${reset} npx qwen-seed [options]
|
|
103
84
|
|
|
104
85
|
${yellow}Options:${reset}
|
|
105
|
-
${cyan}-
|
|
106
|
-
${cyan}-
|
|
107
|
-
${cyan}-
|
|
86
|
+
${cyan}-g, --global${reset} Install globally (to Qwen config directory)
|
|
87
|
+
${cyan}-l, --local${reset} Install locally (to ./.qwen/ in current directory)
|
|
88
|
+
${cyan}-c, --config-dir <path>${reset} Specify custom Qwen config directory
|
|
89
|
+
${cyan}-h, --help${reset} Show this help message
|
|
108
90
|
|
|
109
91
|
${yellow}Examples:${reset}
|
|
110
|
-
${dim}# Install globally (
|
|
111
|
-
npx qwen-seed
|
|
92
|
+
${dim}# Install globally (recommended)${reset}
|
|
93
|
+
npx qwen-seed --global
|
|
112
94
|
|
|
113
95
|
${dim}# Install to current project only${reset}
|
|
114
96
|
npx qwen-seed --local
|
|
@@ -116,70 +98,93 @@ if (hasHelp) {
|
|
|
116
98
|
${yellow}What gets installed:${reset}
|
|
117
99
|
${cyan}commands/qwen-seed/${reset}
|
|
118
100
|
seed.md Entry point (routing + persona)
|
|
119
|
-
tasks/ 5 task files
|
|
120
|
-
data/ 15 type-specific data files
|
|
101
|
+
tasks/ 5 task files
|
|
102
|
+
data/ 15 type-specific data files
|
|
121
103
|
templates/ 5 PLANNING.md output templates
|
|
122
104
|
checklists/ Planning quality gate
|
|
123
105
|
`);
|
|
124
106
|
process.exit(0);
|
|
125
107
|
}
|
|
126
108
|
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.QWEN_CONFIG_DIR);
|
|
130
|
-
const globalDir = configDir || path.join(os.homedir(), '.qwen');
|
|
131
|
-
const qwenDir =
|
|
132
|
-
const seedDest = path.join(qwenDir, 'commands', 'qwen-seed');
|
|
133
|
-
|
|
134
|
-
const locationLabel =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
109
|
+
function install(isGlobal) {
|
|
110
|
+
const src = path.join(__dirname, '..');
|
|
111
|
+
const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.QWEN_CONFIG_DIR);
|
|
112
|
+
const globalDir = configDir || path.join(os.homedir(), '.qwen');
|
|
113
|
+
const qwenDir = isGlobal ? globalDir : path.join(process.cwd(), '.qwen');
|
|
114
|
+
const seedDest = path.join(qwenDir, 'commands', 'qwen-seed');
|
|
115
|
+
|
|
116
|
+
const locationLabel = isGlobal
|
|
117
|
+
? seedDest.replace(os.homedir(), '~')
|
|
118
|
+
: seedDest.replace(process.cwd(), '.');
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(seedDest)) {
|
|
121
|
+
console.log(` ${yellow}Existing installation found at ${locationLabel}${reset}`);
|
|
122
|
+
console.log(` Updating...`);
|
|
123
|
+
fs.rmSync(seedDest, { recursive: true, force: true });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
|
|
127
|
+
|
|
128
|
+
fs.mkdirSync(seedDest, { recursive: true });
|
|
129
|
+
fs.copyFileSync(path.join(src, 'seed.md'), path.join(seedDest, 'seed.md'));
|
|
130
|
+
console.log(` ${green}+${reset} seed.md ${dim}(entry point)${reset}`);
|
|
131
|
+
|
|
132
|
+
const tasksSrc = path.join(src, 'tasks');
|
|
133
|
+
const tasksDest = path.join(seedDest, 'tasks');
|
|
134
|
+
copyDir(tasksSrc, tasksDest);
|
|
135
|
+
console.log(` ${green}+${reset} tasks/ ${dim}(${countFiles(tasksSrc, '.md')} task files)${reset}`);
|
|
144
136
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
copyDir(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// Copy data
|
|
163
|
-
const dataSrc = path.join(src, 'data');
|
|
164
|
-
const dataDest = path.join(seedDest, 'data');
|
|
165
|
-
copyDir(dataSrc, dataDest);
|
|
166
|
-
const dataCount = countFiles(dataSrc, '.md');
|
|
167
|
-
const typeCount = fs.readdirSync(dataSrc, { withFileTypes: true }).filter(e => e.isDirectory()).length;
|
|
168
|
-
console.log(` ${green}+${reset} data/ ${dim}(${typeCount} types, ${dataCount} files)${reset}`);
|
|
169
|
-
|
|
170
|
-
// Copy templates
|
|
171
|
-
const templatesSrc = path.join(src, 'templates');
|
|
172
|
-
const templatesDest = path.join(seedDest, 'templates');
|
|
173
|
-
copyDir(templatesSrc, templatesDest);
|
|
174
|
-
const templateCount = countFiles(templatesSrc, '.md');
|
|
175
|
-
console.log(` ${green}+${reset} templates/ ${dim}(${templateCount} planning templates)${reset}`);
|
|
176
|
-
|
|
177
|
-
// Copy checklists
|
|
178
|
-
const checklistsSrc = path.join(src, 'checklists');
|
|
179
|
-
const checklistsDest = path.join(seedDest, 'checklists');
|
|
180
|
-
copyDir(checklistsSrc, checklistsDest);
|
|
181
|
-
console.log(` ${green}+${reset} checklists/ ${dim}(planning quality gate)${reset}`);
|
|
182
|
-
|
|
183
|
-
console.log(`
|
|
137
|
+
const dataSrc = path.join(src, 'data');
|
|
138
|
+
const dataDest = path.join(seedDest, 'data');
|
|
139
|
+
copyDir(dataSrc, dataDest);
|
|
140
|
+
const typeCount = fs.readdirSync(dataSrc, { withFileTypes: true }).filter(e => e.isDirectory()).length;
|
|
141
|
+
console.log(` ${green}+${reset} data/ ${dim}(${typeCount} types, ${countFiles(dataSrc, '.md')} files)${reset}`);
|
|
142
|
+
|
|
143
|
+
const templatesSrc = path.join(src, 'templates');
|
|
144
|
+
const templatesDest = path.join(seedDest, 'templates');
|
|
145
|
+
copyDir(templatesSrc, templatesDest);
|
|
146
|
+
console.log(` ${green}+${reset} templates/ ${dim}(${countFiles(templatesSrc, '.md')} templates)${reset}`);
|
|
147
|
+
|
|
148
|
+
const checklistsSrc = path.join(src, 'checklists');
|
|
149
|
+
const checklistsDest = path.join(seedDest, 'checklists');
|
|
150
|
+
copyDir(checklistsSrc, checklistsDest);
|
|
151
|
+
console.log(` ${green}+${reset} checklists/ ${dim}(planning quality gate)${reset}`);
|
|
152
|
+
|
|
153
|
+
console.log(`
|
|
184
154
|
${green}Done!${reset} Open Qwen Code and type ${cyan}/seed${reset} to start.
|
|
185
155
|
`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function promptLocation() {
|
|
159
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
160
|
+
|
|
161
|
+
const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.QWEN_CONFIG_DIR);
|
|
162
|
+
const globalPath = configDir || path.join(os.homedir(), '.qwen');
|
|
163
|
+
const globalLabel = globalPath.replace(os.homedir(), '~');
|
|
164
|
+
|
|
165
|
+
console.log(` ${yellow}Where would you like to install?${reset}
|
|
166
|
+
|
|
167
|
+
${cyan}1${reset}) Global ${dim}(${globalLabel})${reset} - available in all projects
|
|
168
|
+
${cyan}2${reset}) Local ${dim}(./.qwen)${reset} - this project only
|
|
169
|
+
`);
|
|
170
|
+
|
|
171
|
+
rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
|
|
172
|
+
rl.close();
|
|
173
|
+
const choice = answer.trim() || '1';
|
|
174
|
+
install(choice !== '2');
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (hasGlobal && hasLocal) {
|
|
179
|
+
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
} else if (explicitConfigDir && hasLocal) {
|
|
182
|
+
console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
} else if (hasGlobal) {
|
|
185
|
+
install(true);
|
|
186
|
+
} else if (hasLocal) {
|
|
187
|
+
install(false);
|
|
188
|
+
} else {
|
|
189
|
+
promptLocation();
|
|
190
|
+
}
|